code: def load_sdf_files(
      input_files: List[str],
      clean_mols: bool = True,
      tasks: List[str] = [],
      shard_size: Optional[int] = None,
  ) -> Iterator[pd.DataFrame]:
      """Load SDF file into dataframe.
  
      Parameters
      ----------
      input_files: List[str]
          List of filenames
      clean_mols: bool, default True
          Whether to sanitize molecules.
      tasks: List[str], default []
          Each entry in `tasks` is treated as a property in the SDF file and is
          retrieved with `mol.GetProp(str(task))` where `mol` is the RDKit mol
          loaded from a given SDF entry.
      shard_size: int, default None
          The shard size to yield at one time.
  
      Returns
      -------
      Iterator[pd.DataFrame]
          Generator which yields the dataframe which is the same shard size.
  
      Notes
      -----
      This function requires RDKit to be installed.
      """
      try:
          from rdkit import Chem
      except ModuleNotFoundError:
          raise ImportError(
              "This function requires RDKit to be installed."
          )
  
      df_rows = []
      for input_file in input_files:
          # Tasks are either in .sdf.csv file or in the .sdf file itself for QM9 dataset
          has_csv = os.path.isfile(input_file + ".csv")
          # Structures are stored in .sdf file
          logger.info("Reading structures from %s." % input_file)
  
          suppl = Chem.SDMolSupplier(
              str(input_file),
              sanitize=clean_mols,
              removeHs=False,
              strictParsing=False,
          )
          for ind, mol in enumerate(suppl):
              if mol is None:
                  continue
              smiles = Chem.MolToSmiles(mol)
              df_row = [ind, smiles, mol]
              if not has_csv:  # Get task targets from .sdf file
                  for task in tasks:
                      df_row.append(mol.GetProp(str(task)))
  
              conf = mol.GetConformer()
              positions = conf.GetPositions()
              pos_x, pos_y, pos_z = zip(*positions)
              df_row.append(str(pos_x))
              df_row.append(str(pos_y))
              df_row.append(str(pos_z))
              df_rows.append(df_row)
  
              if shard_size is not None and len(df_rows) == shard_size:
                  if has_csv:
                      mol_df = pd.DataFrame(
                          df_rows,
                          columns=(
                              "mol_id",
                              "smiles",
                              "mol",
                              "pos_x",
                              "pos_y",
                              "pos_z",
                          ),
                      )
                      raw_df = next(
                          load_csv_files(
                              [input_file + ".csv"], shard_size=None
                          )
                      )
                      yield pd.concat(
                          [mol_df, raw_df], axis=1, join="inner"
                      )
                  else:
                      # Note: Here, the order of columns is based on the order in which the values
                      # are appended to `df_row`. Since pos_x, pos_y, pos_z are appended after appending
                      # tasks above, they occur after `tasks` here.
                      # FIXME Ideally, we should use something like a dictionary here to keep it independent
                      # of column ordering.
                      mol_df = pd.DataFrame(
                          df_rows,
                          columns=("mol_id", "smiles", "mol")
                          + tuple(tasks)
                          + ("pos_x", "pos_y", "pos_z"),
                      )
                      yield mol_df
                  # Reset aggregator
                  df_rows = []
  
          # Handle final leftovers for this file
          if len(df_rows) > 0:
              if has_csv:
                  mol_df = pd.DataFrame(
                      df_rows,
                      columns=(
                          "mol_id",
                          "smiles",
                          "mol",
                          "pos_x",
                          "pos_y",
                          "pos_z",
                      ),
                  )
                  raw_df = next(
                      load_csv_files(
                          [input_file + ".csv"], shard_size=None
                      )
                  )
                  yield pd.concat(
                      [mol_df, raw_df], axis=1, join="inner"
                  )
              else:
                  mol_df = pd.DataFrame(
                      df_rows,
                      columns=("mol_id", "smiles", "mol")
                      + tuple(tasks)
                      + ("pos_x", "pos_y", "pos_z"),
                  )
                  yield mol_df
              df_rows = []
  
imports: import joblib
  import gzip
  import pickle
  import os
  import tempfile
  import tarfile
  import zipfile
  import logging
  from urllib.request import urlretrieve
  from typing import Any, Iterator, List, Optional, Tuple, Union, cast, IO
  import pandas
  import numpy
  import deepchem
label_desc: Use image node_id 7 for calling a global function with example usage: load_sdf_files(input_files, clean_mols, tasks, shard_size) without return types