code: def _get_statistics(self, artifacts_dict=None):
      # type: (Optional[Dict[str, Artifact]]) -> str
      summary = ""
      artifacts_dict = artifacts_dict or self._artifacts_container
      thread_pool = ThreadPool()
  
      try:
          # build hash row sets
          artifacts_summary = []
          for a_name, a_df in artifacts_dict.items():
              hash_cols = self._artifacts_container.get_hash_columns(
                  a_name
              )
              if not pd or not isinstance(a_df, pd.DataFrame):
                  continue
  
              if hash_cols is True:
                  hash_col_drop = []
              else:
                  hash_cols = set(hash_cols)
                  missing_cols = hash_cols.difference(a_df.columns)
                  if missing_cols == hash_cols:
                      LoggerRoot.get_base_logger().warning(
                          "Uniqueness columns {} not found in artifact {}. "
                          "Skipping uniqueness check for artifact.".format(
                              list(missing_cols), a_name
                          )
                      )
                      continue
                  elif missing_cols:
                      # missing_cols must be a subset of hash_cols
                      hash_cols.difference_update(missing_cols)
                      LoggerRoot.get_base_logger().warning(
                          "Uniqueness columns {} not found in artifact {}. Using {}.".format(
                              list(missing_cols),
                              a_name,
                              list(hash_cols),
                          )
                      )
  
                  hash_col_drop = [
                      col
                      for col in a_df.columns
                      if col not in hash_cols
                  ]
  
              a_unique_hash = set()
  
              def hash_row(r):
                  a_unique_hash.add(hash(bytes(r)))
  
              a_shape = a_df.shape
              # parallelize
              a_hash_cols = a_df.drop(columns=hash_col_drop)
              thread_pool.map(hash_row, a_hash_cols.values)
              # add result
              artifacts_summary.append(
                  (
                      a_name,
                      a_shape,
                      a_unique_hash,
                  )
              )
  
          # build intersection summary
          for i, (name, shape, unique_hash) in enumerate(
              artifacts_summary
          ):
              summary += "[{name}]: shape={shape}, {unique} unique rows, {percentage:.1f}% uniqueness\n".format(
                  name=name,
                  shape=shape,
                  unique=len(unique_hash),
                  percentage=100 * len(unique_hash) / float(shape[0]),
              )
              for name2, shape2, unique_hash2 in artifacts_summary[
                  i + 1 :
              ]:
                  intersection = len(unique_hash & unique_hash2)
                  summary += "\tIntersection with [{name2}] {intersection} rows: {percentage:.1f}%\n".format(
                      name2=name2,
                      intersection=intersection,
                      percentage=100
                      * intersection
                      / float(len(unique_hash2)),
                  )
      except Exception as e:
          LoggerRoot.get_base_logger().warning(str(e))
      finally:
          thread_pool.close()
          thread_pool.terminate()
      return summary
  
imports: import gzip
  import io
  import json
  import yaml
  import mimetypes
  import os
  import pickle
  from six.moves.urllib.parse import quote
  from copy import deepcopy
  from datetime import datetime
  from multiprocessing.pool import ThreadPool
  from tempfile import mkdtemp, mkstemp
  from threading import Thread
  from time import time
  from zipfile import ZipFile, ZIP_DEFLATED
  import six
  from PIL import Image
  from pathlib2 import Path
  from six.moves.urllib.parse import urlparse
  from typing import Dict, Union, Optional, Any, Sequence, Callable
  from ..backend_api import Session
  from ..backend_api.services import tasks
  from ..backend_interface.metrics.events import UploadEvent
  from ..debugging.log import LoggerRoot
  from ..storage.helper import remote_driver_schemes
  from ..storage.util import sha256sum, format_size, get_common_path
  from ..utilities.process.mp import SafeEvent, ForkSafeRLock
  from ..utilities.proxy_object import LazyEvalWrapper
label_desc: Use image node_id 14 for calling the Artifacts obj's underlying member method code with example usage: obj._get_statistics(artifacts_dict) and returns: summary