Instruction:
Given the following Python 3 function, write the documentation
def plot_summary_figure(info_df, summary_df, color_list, symbol_list,
                        selected_summaries,
                        batch_dir, batch_name, plot_style=None, show=False,
                        save=True,
                        figure_type=None):
    """Create a figure with summary graphs.
    Args:
        info_df: the pandas DataFrame with info about the runs.
        summary_df: a pandas DataFrame with the summary data.
        color_list: a list of colors to use (one pr. group)
        symbol_list: a list of symbols to use (one pr. cell in largest group)
        selected_summaries: a list of the selected summaries to plot
        batch_dir: path to the folder where the figure should be saved.
        batch_name: the batch name.
        plot_style: the matplotlib plot-style to use.
        show: show the figure if True.
        save: save the figure if True.
        figure_type: a string for selecting type of figure to make.
    """
    figure_type_object = figure_types[figure_type]

    logger.debug("creating figure ({})".format(figure_type))
    standard_fig, ax = plt.subplots(nrows=figure_type_object.rows,
                                    ncols=figure_type_object.columns,
                                    sharex=True)

    ce_ax = figure_type_object.retrieve_axis("ce_ax", ax)
    cap_ax = figure_type_object.retrieve_axis("cap_ax", ax)
    ir_ax = figure_type_object.retrieve_axis("ir_ax", ax)
    ev_ax = figure_type_object.retrieve_axis("ev_ax", ax)

    # pick data (common for all plot types)
    # could include a if cd_ax: pick_summary_data...
    ce_df = pick_summary_data("coulombic_efficiency", summary_df,
                              selected_summaries)
    cc_df = pick_summary_data("charge_capacity", summary_df, selected_summaries)
    dc_df = pick_summary_data("discharge_capacity", summary_df,
                              selected_summaries)

    # generate labels
    ce_labels = [info_df.get_value(filename, "labels") for filename in
                 ce_df.columns.get_level_values(0)]

    # adding charge/discharge label
    ce_labels.extend(["", "discharge", "charge"])

    # plot ce
    list_of_lines, plot_style = plot_summary_data(ce_ax, ce_df, info_df=info_df,
                                                  color_list=color_list,
                                                  symbol_list=symbol_list,
                                                  is_charge=False,
                                                  plot_style=plot_style)
    ce_ax.set_ylabel("Coulombic\nefficiency\n(%)")
    ce_ax.locator_params(nbins=5)

    # adding charge/discharge label
    color = plot_style["color"]
    markersize = plot_style["markersize"]

    open_label = mpl.lines.Line2D([], [], color=color, marker='s',
                                  markeredgecolor=color, markerfacecolor='none',
                                  markersize=markersize)
    closed_label = mpl.lines.Line2D([], [], color=color, marker='s',
                                    markeredgecolor=color,
                                    markerfacecolor=color,
                                    markersize=markersize)
    no_label = mpl.lines.Line2D([], [], color='none', marker='s', markersize=0)
    list_of_lines.extend([no_label, closed_label, open_label])

    # plotting capacity (common)
    plot_summary_data(cap_ax, cc_df, is_charge=True, info_df=info_df,
                      color_list=color_list,
                      symbol_list=symbol_list, plot_style=plot_style)
    plot_summary_data(cap_ax, dc_df, is_charge=False, info_df=info_df,
                      color_list=color_list,
                      symbol_list=symbol_list, plot_style=plot_style)
    cap_ax.set_ylabel("Capacity\n(mAh/g)")
    cap_ax.locator_params(nbins=4)

    # plotting ir data (common)
    plot_ir_charge, plot_ir_discharge = figure_type_object.ir_selector
    if plot_ir_charge:
        irc_df = pick_summary_data("ir_charge", summary_df, selected_summaries)
        plot_summary_data(ir_ax, irc_df, is_charge=True, info_df=info_df,
                          color_list=color_list,
                          symbol_list=symbol_list, plot_style=plot_style)
    if plot_ir_discharge:
        ird_df = pick_summary_data("ir_discharge", summary_df,
                                   selected_summaries)
        plot_summary_data(ir_ax, ird_df, is_charge=False, info_df=info_df,
                          color_list=color_list,
                          symbol_list=symbol_list, plot_style=plot_style)

    ir_ax.set_ylabel("Internal\nresistance\n(Ohms)")
    ir_ax.set_xlabel("Cycle number")
    ir_ax.locator_params(axis="y", nbins=4)
    ir_ax.locator_params(axis="x", nbins=10)
    # should use MaxNLocator here instead

    # pick data (not common for all plot types)
    if ev_ax is not None:
        plot_ev_charge, plot_ev_discharge = figure_type_object\
            .end_voltage_selector
        if plot_ev_charge:
            evc_df = pick_summary_data("end_voltage_charge", summary_df,
                                       selected_summaries)
            plot_summary_data(ev_ax, evc_df, is_charge=True, info_df=info_df,
                              color_list=color_list,
                              symbol_list=symbol_list, plot_style=plot_style)
        if plot_ev_discharge:
            evd_df = pick_summary_data("end_voltage_discharge", summary_df,
                                       selected_summaries)
            plot_summary_data(ev_ax, evd_df, is_charge=False, info_df=info_df,
                              color_list=color_list,
                              symbol_list=symbol_list, plot_style=plot_style)

        ev_ax.set_ylabel("End\nvoltage\n(V)")
        ev_ax.locator_params(axis="y", nbins=4)

    # tweaking
    plt.subplots_adjust(left=0.07, right=0.93, top=0.9, wspace=0.25,
                        hspace=0.15)

    # adding legend
    logger.debug("trying to add legends " + str(ce_labels))
    standard_fig.legend(handles=list_of_lines, labels=ce_labels,
                        bbox_to_anchor=(1.02, 1.1), loc=2,
                        # bbox_transform=plt.gcf().transFigure,
                        bbox_transform=ce_ax.transAxes,
                        numpoints=1,
                        ncol=1, labelspacing=0.,
                        prop={"size": 10})

    # plt.tight_layout()
    if save:
        extension = prms.Batch["fig_extension"]
        dpi = prms.Batch["dpi"]
        figure_file_name = os.path.join(batch_dir, "%splot_%s.%s" % (
        figure_type, batch_name, extension))
        standard_fig.savefig(figure_file_name, dpi=dpi, bbox_inches='tight')
    if show:
        plt.show()
    return standard_fig, (ce_ax, cap_ax, ir_ax)

Response: Create a figure with summary graphs.


Instruction:
How would you explain what the following Python 3 function does
def export_dqdv(cell_data, savedir, sep, last_cycle=None):
    logger.debug("exporting dqdv")
    filename = cell_data.dataset.loaded_from
    no_merged_sets = ""
    firstname, extension = os.path.splitext(filename)
    firstname += no_merged_sets
    if savedir:
        firstname = os.path.join(savedir, os.path.basename(firstname))
        logger.debug(f"savedir is true: {firstname}")

    outname_charge = firstname + "_dqdv_charge.csv"
    outname_discharge = firstname + "_dqdv_discharge.csv"

    list_of_cycles = cell_data.get_cycle_numbers()
    number_of_cycles = len(list_of_cycles)
    logger.debug("%s: you have %i cycles" % (filename, number_of_cycles))

    # extracting charge
    out_data = _extract_dqdv(cell_data, cell_data.get_ccap, last_cycle)
    logger.debug("extracted ica for charge")
    try:
        _save_multi(data=out_data, file_name=outname_charge, sep=sep)
    except ExportFailed:
        logger.info("could not export ica for charge")
    else:
        logger.debug("saved ica for charge")

    # extracting discharge
    out_data = _extract_dqdv(cell_data, cell_data.get_dcap, last_cycle)
    logger.debug("extracxted ica for discharge")
    try:
        _save_multi(data=out_data, file_name=outname_discharge, sep=sep)
    except ExportFailed:
        logger.info("could not export ica for discharge")
    else:
        logger.debug("saved ica for discharge")

Response: Exports dQ / dV data from a CellpyData instance.


Instruction:
Implement a function in Python 3 to
return an initialized instance of the Batch class

Response:
def init(*args, **kwargs):
    """Returns an initialized instance of the Batch class"""
    # set up cellpy logger
    default_log_level = kwargs.pop("default_log_level", None)
    import cellpy.log as log
    log.setup_logging(custom_log_dir=prms.Paths["filelogdir"],
                      default_level=default_log_level)
    b = Batch(*args, **kwargs)
    return b


Instruction:
Write a Python 3 script for
creating a DataFrame with info about the runs

Response:
def create_info_df(self):
        """Creates a DataFrame with info about the runs (loaded from the DB)"""
        logger.debug("running create_info_df")
        # initializing the reader
        reader = self.reader()
        self.info_df = make_df_from_batch(self.name, batch_col=self.batch_col,
                                          reader=reader)
        logger.debug(str(self.info_df.head(5)))


Instruction:
Can you write a function in Python 3 where it
saves the DataFrame with info about the runs to a JSON file

Response:
def save_info_df(self):
        """Saves the DataFrame with info about the runs to a JSON file"""
        logger.debug("running save_info_df")

        info_df = self.info_df
        top_level_dict = {'info_df': info_df, 'metadata': self._prm_packer()}

        # packing prms

        jason_string = json.dumps(top_level_dict,
                                  default=lambda info_df: json.loads(
                                      info_df.to_json()))
        with open(self.info_file, 'w') as outfile:
            outfile.write(jason_string)
        logger.info("Saved file to {}".format(self.info_file))


Instruction:
Can you generate a brief explanation for the following Python 3 code
def load_info_df(self, file_name=None):

        if file_name is None:
            file_name = self.info_file

        with open(file_name, 'r') as infile:
            top_level_dict = json.load(infile)

        new_info_df_dict = top_level_dict['info_df']
        new_info_df = pd.DataFrame(new_info_df_dict)
        self.info_df = new_info_df

        self._prm_packer(top_level_dict['metadata'])
        self.info_file = file_name
        logger.debug("loaded info_df")
        logger.debug(" info_file: %s" % self.info_file)

Response: Loads a DataFrame with all the needed info about the run


Instruction:
Write a Python 3 script to
create a folder structure based on the project and batch name.

Response:
def create_folder_structure(self):
        """Creates a folder structure based on the project and batch name.

        Project - Batch-name - Raw-data-dir

        The info_df JSON-file will be stored in the Project folder.
        The summary-files will be saved in the Batch-name folder.
        The raw data (including exported cycles and ica-data) will be saved to
        the Raw-data-dir.

        """
        self.info_file, directories = create_folder_structure(self.project,
                                                              self.name)
        self.project_dir, self.batch_dir, self.raw_dir = directories
        logger.debug("create folders:" + str(directories))


Instruction:
Can you tell what is the following Python 3 function doing
def load_and_save_raw(self, parent_level="CellpyData"):
        sep = prms.Reader["sep"]
        if self.use_cellpy_stat_file is None:
            use_cellpy_stat_file = prms.Reader.use_cellpy_stat_file
        else:
            use_cellpy_stat_file = self.use_cellpy_stat_file
        logger.debug(f"b.load_and_save_raw: "
                     f"use_cellpy_stat_file = {use_cellpy_stat_file}")
        self.frames, self.keys, errors = read_and_save_data(
            self.info_df,
            self.raw_dir,
            sep=sep,
            force_raw=self.force_raw_file,
            force_cellpy=self.force_cellpy_file,
            export_cycles=self.export_cycles,
            shifted_cycles=self.shifted_cycles,
            export_raw=self.export_raw,
            export_ica=self.export_ica,
            save=self.save_cellpy_file,
            use_cellpy_stat_file=use_cellpy_stat_file,
            parent_level=parent_level,
            last_cycle=self.last_cycle
        )
        logger.debug("loaded and saved data. errors:" + str(errors))

Response: Loads the cellpy or raw - data file and saves to csv


Instruction:
How would you implement a function in Python 3 that
makes and save summary csv files

Response:
def make_summaries(self):
        """Make and save summary csv files,
        each containing values from all cells"""
        self.summary_df = save_summaries(self.frames, self.keys,
                                         self.selected_summaries,
                                         self.batch_dir, self.name)
        logger.debug("made and saved summaries")


Instruction:
Implement a function in Python 3 to
plot the summary graphs for the current object.

Response:
def plot_summaries(self, show=False, save=True, figure_type=None):
        """Plot summary graphs.

        Args:
            show: shows the figure if True.
            save: saves the figure if True.
            figure_type: optional, figure type to create.
        """

        if not figure_type:
            figure_type = self.default_figure_type

        if not figure_type in self.default_figure_types:
            logger.debug("unknown figure type selected")
            figure_type = self.default_figure_type

        color_list, symbol_list = self._create_colors_markers_list()
        summary_df = self.summary_df
        selected_summaries = self.selected_summaries
        batch_dir = self.batch_dir
        batch_name = self.name
        fig, ax = plot_summary_figure(self.info_df, summary_df, color_list,
                                      symbol_list, selected_summaries,
                                      batch_dir, batch_name, show=show,
                                      save=save, figure_type=figure_type)
        self.figure[figure_type] = fig
        self.axes[figure_type] = ax


Instruction:
Create a Python 3 function to
update the selected datasets.

Response:
def update(self, all_in_memory=None):
        """Updates the selected datasets.

        Args:
            all_in_memory (bool): store the cellpydata in memory (default
            False)

        """
        logging.info("[update experiment]")
        if all_in_memory is not None:
            self.all_in_memory = all_in_memory

        pages = self.journal.pages
        summary_frames = dict()
        cell_data_frames = dict()
        number_of_runs = len(pages)
        counter = 0
        errors = []

        for indx, row in pages.iterrows():
            counter += 1
            h_txt = "[" + counter * "|" + (
                    number_of_runs - counter) * "." + "]"
            l_txt = "starting to process file # %i (index=%s)" % (counter, indx)
            logging.debug(l_txt)
            print(h_txt)

            if not row.raw_file_names and not self.force_cellpy:
                logging.info("File(s) not found!")
                logging.info(indx)
                logging.debug("File(s) not found for index=%s" % indx)
                errors.append(indx)
                continue

            else:
                logging.info(f"Processing {indx}")

            cell_data = cellreader.CellpyData()
            if not self.force_cellpy or self.force_recalc:
                logging.info(
                    "setting cycle mode (%s)..." % row.cell_type)
                cell_data.cycle_mode = row.cell_type

            logging.info("loading cell")
            if not self.force_cellpy:
                logging.debug("not forcing to load cellpy-file instead of raw file.")
                try:
                    cell_data.loadcell(
                        raw_files=row.raw_file_names,
                        cellpy_file=row.cellpy_file_names,
                        mass=row.masses,
                        summary_on_raw=True,
                        force_raw=self.force_raw_file,
                        use_cellpy_stat_file=prms.Reader.use_cellpy_stat_file
                    )
                except Exception as e:
                    logging.info('Failed to load: ' + str(e))
                    errors.append("loadcell:" + str(indx))
                    if not self.accept_errors:
                        raise e
                    continue

            else:
                logging.info("forcing")
                try:
                    cell_data.load(row.cellpy_file_names,
                                   parent_level=self.parent_level)
                except Exception as e:
                    logging.info(
                        f"Critical exception encountered {type(e)} "
                        "- skipping this file")
                    logging.debug(
                        'Failed to load. Error-message: ' + str(e))
                    errors.append("load:" + str(indx))
                    if not self.accept_errors:
                        raise e
                    continue

            if not cell_data.check():
                logging.info("...not loaded...")
                logging.debug(
                    "Did not pass check(). Could not load cell!")
                errors.append("check:" + str(indx))
                continue

            logging.info("...loaded successfully...")

            summary_tmp = cell_data.dataset.dfsummary
            logging.info("Trying to get summary_data")

            if cell_data.dataset.step_table is None or self.force_recalc:
                logging.info(
                    "Running make_step_table"
                )

                cell_data.make_step_table()

            if summary_tmp is None or self.force_recalc:
                logging.info(
                    "Running make_summary"
                )

                cell_data.make_summary(find_end_voltage=True,
                                       find_ir=True)

            if summary_tmp.index.name == b"Cycle_Index":
                logging.debug("Strange: 'Cycle_Index' is a byte-string")
                summary_tmp.index.name = 'Cycle_Index'

            if not summary_tmp.index.name == "Cycle_Index":
                logging.debug("Setting index to Cycle_Index")
                # check if it is a byte-string
                if b"Cycle_Index" in summary_tmp.columns:
                    logging.debug(
                        "Seems to be a byte-string in the column-headers")
                    summary_tmp.rename(
                        columns={b"Cycle_Index": 'Cycle_Index'},
                        inplace=True)
                summary_tmp.set_index("Cycle_Index", inplace=True)

            summary_frames[indx] = summary_tmp

            if self.all_in_memory:
                cell_data_frames[indx] = cell_data
            else:
                cell_data_frames[indx] = cellreader.CellpyData(initialize=True)
                cell_data_frames[indx].dataset.step_table = \
                    cell_data.dataset.step_table
                # cell_data_frames[indx].dataset.step_table_made = True

            if self.save_cellpy:
                logging.info("saving to cellpy-format")
                if not row.fixed:
                    logging.info("saving cell to %s" % row.cellpy_file_names)
                    cell_data.ensure_step_table = True
                    cell_data.save(row.cellpy_file_names)
                else:
                    logging.debug(
                        "saving cell skipped (set to 'fixed' in info_df)")

            if self.export_raw or self.export_cycles:
                export_text = "exporting"
                if self.export_raw:
                    export_text += " [raw]"
                if self.export_cycles:
                    export_text += " [cycles]"
                logging.info(export_text)
                cell_data.to_csv(
                    self.journal.raw_dir,
                    sep=prms.Reader.sep,
                    cycles=self.export_cycles,
                    shifted=self.shifted_cycles,
                    raw=self.export_raw,
                    last_cycle=self.last_cycle
                )

            if self.export_ica:
                logging.info("exporting [ica]")
                try:
                    helper.export_dqdv(
                        cell_data,
                        savedir=self.journal.raw_dir,
                        sep=prms.Reader.sep,
                        last_cycle=self.last_cycle
                    )
                except Exception as e:
                    logging.error(
                        "Could not make/export dq/dv data"
                    )
                    logging.debug(
                        "Failed to make/export "
                        "dq/dv data (%s): %s" % (indx, str(e))
                    )
                    errors.append("ica:" + str(indx))

        self.errors["update"] = errors
        self.summary_frames = summary_frames
        self.cell_data_frames = cell_data_frames


Instruction:
Can you write a function in Python 3 where it
ensures that an appropriate link to the cellpy - files exists for each cell.

Response:
def link(self):
        """Ensure that an appropriate link to the cellpy-files exists for
        each cell.

        The experiment will then contain a CellpyData object for each cell
        (in the cell_data_frames attribute) with only the step-table stored.

        Remark that running update persists the summary frames instead (or
        everything in case you specify all_in_memory=True).
        This might be considered "a strange and unexpected behaviour". Sorry
        for that (but the authors of this package is also a bit strange...).

        """
        logging.info("[estblishing links]")
        logging.debug("checking and establishing link to data")
        cell_data_frames = dict()
        counter = 0
        errors = []
        try:
            for indx, row in self.journal.pages.iterrows():

                counter += 1
                l_txt = "starting to process file # %i (index=%s)" % (counter, indx)
                logging.debug(l_txt)
                logging.info(f"linking cellpy-file: {row.cellpy_file_names}")

                if not os.path.isfile(row.cellpy_file_names):
                    logging.error("File does not exist")
                    raise IOError

                cell_data_frames[indx] = cellreader.CellpyData(initialize=True)

                step_table = helper.look_up_and_get(
                    row.cellpy_file_names,
                    "step_table"
                )

                cell_data_frames[indx].dataset.step_table = step_table

            self.cell_data_frames = cell_data_frames

        except IOError as e:
            logging.warning(e)
            e_txt = "links not established - try update"
            logging.warning(e_txt)
            errors.append(e_txt)

        self.errors["link"] = errors


Instruction:
How would you explain what the following Python 3 function does
def get_default_config_file_path(init_filename=None):
    prm_dir = get_package_prm_dir()
    if not init_filename:
        init_filename = DEFAULT_FILENAME
    src = os.path.join(prm_dir, init_filename)
    return src

Response: gets the path to the default config - file


Instruction:
Can you tell what is the following Python 3 function doing
def get_user_dir_and_dst(init_filename):
    user_dir = get_user_dir()
    dst_file = os.path.join(user_dir, init_filename)
    return user_dir, dst_file

Response: gets the name of the user directory and full prm filepath


Instruction:
How would you explain what the following Python 3 function does
def setup(interactive, not_relative, dry_run, reset, root_dir, testuser):

    click.echo("[cellpy] (setup)")

    # generate variables
    init_filename = create_custom_init_filename()
    userdir, dst_file = get_user_dir_and_dst(init_filename)

    if testuser:
        if not root_dir:
            root_dir = os.getcwd()

        click.echo(f"[cellpy] (setup) DEV-MODE testuser: {testuser}")
        init_filename = create_custom_init_filename(testuser)
        userdir = root_dir
        dst_file = get_dst_file(userdir, init_filename)
        click.echo(f"[cellpy] (setup) DEV-MODE userdir: {userdir}")
        click.echo(f"[cellpy] (setup) DEV-MODE dst_file: {dst_file}")

    if not pathlib.Path(dst_file).is_file():
        reset = True

    if interactive:
        click.echo(" interactive mode ".center(80, "-"))
        _update_paths(root_dir, not not_relative, dry_run=dry_run, reset=reset)
        _write_config_file(
            userdir, dst_file,
            init_filename, dry_run,
        )
        _check()

    else:
        _write_config_file(userdir, dst_file, init_filename, dry_run)
        _check()

Response: This function will help you to setup cellpy.


Instruction:
Here you have a function in Python 3, explain what it does
def _parse_g_dir(repo, gdirpath):
    for f in repo.get_contents(gdirpath):
        if f.type == "dir":
            for sf in repo.get_contents(f.path):
                yield sf
        else:
            yield f

Response: parses a repo directory two - levels deep


Instruction:
How would you code a function in Python 3 to
extract table from cellpy hdf5 - file.

Response:
def look_up_and_get(cellpy_file_name, table_name):
    """Extracts table from cellpy hdf5-file."""

    # infoname = '/CellpyData/info'
    # dataname = '/CellpyData/dfdata'
    # summaryname = '/CellpyData/dfsummary'
    # fidname = '/CellpyData/fidtable'
    # stepname = '/CellpyData/step_table'

    root = '/CellpyData'
    table_path = '/'.join([root, table_name])

    logging.debug(f"look_up_and_get({cellpy_file_name}, {table_name}")
    store = pd.HDFStore(cellpy_file_name)
    table = store.select(table_path)
    store.close()
    return table


Instruction:
Here you have a function in Python 3, explain what it does
def fix_groups(groups):
    _groups = []
    for g in groups:
        try:
            if not float(g) > 0:
                _groups.append(1000)
            else:
                _groups.append(int(g))
        except TypeError as e:
            logging.info("Error in reading group number (check your db)")
            logging.debug(g)
            logging.debug(e)
            _groups.append(1000)
    return _groups

Response: Takes care of strange group numbers.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def create_selected_summaries_dict(summaries_list):
    headers_summary = cellpy.parameters.internal_settings.get_headers_summary()
    selected_summaries = dict()
    for h in summaries_list:
        selected_summaries[h] = headers_summary[h]
    return selected_summaries

Response: Creates a dictionary with selected summary column headers.


Instruction:
Implement a Python 3 function for
parsing the summaries and combine based on column selected_summaries

Response:
def join_summaries(summary_frames, selected_summaries, keep_old_header=False):
    """parse the summaries and combine based on column (selected_summaries)"""

    selected_summaries_dict = create_selected_summaries_dict(selected_summaries)
    frames = []
    keys = []
    for key in summary_frames:
        keys.append(key)
        if summary_frames[key].empty:
            logging.debug("Empty summary_frame encountered")
        frames.append(summary_frames[key])

    out = []
    summary_df = pd.concat(frames, keys=keys, axis=1)
    for key, value in selected_summaries_dict.items():
        _summary_df = summary_df.iloc[
                      :, summary_df.columns.get_level_values(1) == value
                      ]
        _summary_df.name = key

        if not keep_old_header:
            try:
                _summary_df.columns = _summary_df.columns.droplevel(-1)
            except AttributeError as e:
                logging.debug("could not drop level from frame")
                logging.debug(e)

        out.append(_summary_df)
    logger.debug("finished joining summaries")

    return out


Instruction:
Implement a function in Python 3 to
create sensible folder names.

Response:
def generate_folder_names(name, project):
    """Creates sensible folder names."""

    out_data_dir = prms.Paths.outdatadir
    project_dir = os.path.join(out_data_dir, project)
    batch_dir = os.path.join(project_dir, name)
    raw_dir = os.path.join(batch_dir, "raw_data")
    return out_data_dir, project_dir, batch_dir, raw_dir


Instruction:
How would you explain what the following Python 3 function does
def group_by_interpolate(df, x=None, y=None, group_by=None,
                         number_of_points=100, tidy=False,
                         individual_x_cols=False, header_name="Unit",
                         dx=10.0, generate_new_x=True):
    """Use this for generating wide format from long (tidy) data"""

    time_00 = time.time()
    if x is None:
        x = HEADERS_NORMAL.step_time_txt
    if y is None:
        y = HEADERS_NORMAL.voltage_txt
    if group_by is None:
        group_by = [HEADERS_NORMAL.cycle_index_txt]

    if not isinstance(group_by, (list, tuple)):
        group_by = [group_by]

    if not generate_new_x:
        # check if it makes sence
        if (not tidy) and (not individual_x_cols):
            logging.warning("Unlogical condition")
            generate_new_x = True

    new_x = None

    if generate_new_x:
        x_max = df[x].max()
        x_min = df[x].min()
        if number_of_points:
            new_x = np.linspace(x_max, x_min, number_of_points)
        else:
            new_x = np.arange(x_max, x_min, dx)

    new_dfs = []
    keys = []

    for name, group in df.groupby(group_by):
        keys.append(name)
        if not isinstance(name, (list, tuple)):
            name = [name]

        new_group = _interpolate_df_col(
            group, x=x, y=y, new_x=new_x,
            number_of_points=number_of_points,
            dx=dx,
        )

        if tidy or (not tidy and not individual_x_cols):
            for i, j in zip(group_by, name):
                new_group[i] = j
        new_dfs.append(new_group)

    if tidy:
        new_df = pd.concat(new_dfs)
    else:
        if individual_x_cols:
            new_df = pd.concat(new_dfs, axis=1, keys=keys)
            group_by.append(header_name)
            new_df.columns.names = group_by
        else:
            new_df = pd.concat(new_dfs)
            new_df = new_df.pivot(index=x, columns=group_by[0], values=y, )
    self.logger.debug(f"(dt: {(time.time() - time_00):4.2f}s)")
    return new_df

Response: This function is used to generate wide format from long data.


Instruction:
Can you create a Python 3 function that
interpolates a column based on another column.

Response:
def _interpolate_df_col(df, x=None, y=None, new_x=None, dx=10.0,
                        number_of_points=None, direction=1, **kwargs):
        """Interpolate a column based on another column.

        Args:
            df: DataFrame with the (cycle) data.
            x: Column name for the x-value (defaults to the step-time column).
            y: Column name for the y-value (defaults to the voltage column).
            new_x (numpy array or None): Interpolate using these new x-values
                instead of generating x-values based on dx or number_of_points.
            dx: step-value (defaults to 10.0)
            number_of_points: number of points for interpolated values (use
                instead of dx and overrides dx if given).
            direction (-1,1): if direction is negetive, then invert the
                x-values before interpolating.
            **kwargs: arguments passed to scipy.interpolate.interp1d

        Returns: DataFrame with interpolated y-values based on given or
            generated x-values.

        """

        if x is None:
            x = df.columns[0]
        if y is None:
            y = df.columns[1]

        xs = df[x].values
        ys = df[y].values

        if direction > 0:
            x_min = xs.min()
            x_max = xs.max()
        else:
            x_max = xs.min()
            x_min = xs.max()
            dx = -dx

        bounds_error = kwargs.pop("bounds_error", False)
        f = interpolate.interp1d(xs, ys, bounds_error=bounds_error, **kwargs)
        if new_x is None:
            if number_of_points:
                new_x = np.linspace(x_min, x_max, number_of_points)
            else:
                new_x = np.arange(x_min, x_max, dx)

        new_y = f(new_x)

        new_df = pd.DataFrame(
            {x: new_x, y: new_y}
        )

        return new_df


Instruction:
Write a Python 3 script to
create a list of pandas. DataFrames one for each charge step.

Response:
def _collect_capacity_curves(data, direction="charge"):
    """Create a list of pandas.DataFrames, one for each charge step.

    The DataFrames are named by its cycle number.

    Input: CellpyData
    Returns: list of pandas.DataFrames
        minimum voltage value,
        maximum voltage value"""

    minimum_v_value = np.Inf
    maximum_v_value = -np.Inf
    charge_list = []
    cycles = data.get_cycle_numbers()
    for cycle in cycles:
        try:
            if direction == "charge":
                q, v = data.get_ccap(cycle)
            else:
                q, v = data.get_dcap(cycle)

        except NullData as e:
            logging.warning(e)
            break

        else:
            d = pd.DataFrame({"q": q, "v": v})
            # d.name = f"{cycle}"
            d.name = cycle
            charge_list.append(d)
            v_min = v.min()
            v_max = v.max()
            if v_min < minimum_v_value:
                minimum_v_value = v_min
            if v_max > maximum_v_value:
                maximum_v_value = v_max
    return charge_list, cycles, minimum_v_value, maximum_v_value


Instruction:
How would you explain what the following Python 3 function does
def cell(filename=None, mass=None, instrument=None, logging_mode="INFO",
         cycle_mode=None, auto_summary=True):
    """Create a CellpyData object"""

    from cellpy import log

    log.setup_logging(default_level=logging_mode)
    cellpy_instance = setup_cellpy_instance()

    if instrument is not None:
        cellpy_instance.set_instrument(instrument=instrument)

    if cycle_mode is not None:
        cellpy_instance.cycle_mode = cycle_mode

    if filename is not None:
        filename = Path(filename)

        if filename.suffix in [".h5", ".hdf5", ".cellpy", ".cpy"]:
            logging.info(f"Loading cellpy-file: {filename}")
            cellpy_instance.load(filename)
        else:
            logging.info(f"Loading raw-file: {filename}")
            cellpy_instance.from_raw(filename)
            if mass is not None:
                logging.info("Setting mass")
                cellpy_instance.set_mass(mass)
            if auto_summary:
                logging.info("Creating step table")
                cellpy_instance.make_step_table()
                logging.info("Creating summary data")
                cellpy_instance.make_summary()

    logging.info("Created CellpyData object")
    return cellpy_instance

Response: Create a CellpyData object from a file.


Instruction:
How would you explain what the following Python 3 function does
def just_load_srno(srno, prm_filename=None):
    from cellpy import dbreader, filefinder
    print("just_load_srno: srno: %i" % srno)

    # ------------reading parameters--------------------------------------------
    # print "just_load_srno: read prms"
    # prm = prmreader.read(prm_filename)
    #
    # print prm

    print("just_load_srno: making class and setting prms")
    d = CellpyData()

    # ------------reading db----------------------------------------------------
    print()
    print("just_load_srno: starting to load reader")
    # reader = dbreader.reader(prm_filename)
    reader = dbreader.Reader()
    print("------ok------")

    run_name = reader.get_cell_name(srno)
    print("just_load_srno: run_name:")
    print(run_name)

    m = reader.get_mass(srno)
    print("just_load_srno: mass: %f" % m)
    print()

    # ------------loadcell------------------------------------------------------
    print("just_load_srno: getting file_names")
    raw_files, cellpy_file = filefinder.search_for_files(run_name)
    print("raw_files:", raw_files)
    print("cellpy_file:", cellpy_file)

    print("just_load_srno: running loadcell")
    d.loadcell(raw_files, cellpy_file, mass=m)
    print("------ok------")

    # ------------do stuff------------------------------------------------------
    print("just_load_srno: getting step_numbers for charge")
    v = d.get_step_numbers("charge")
    print(v)

    print()
    print("just_load_srno: finding C-rates")
    d.find_C_rates(v, silent=False)

    print()
    print("just_load_srno: OK")
    return True

Response: This function loads a dataset based on a serial number.


Instruction:
Write a Python 3 function for
loading a raw data file and saves it as a cellpy - file.

Response:
def load_and_save_resfile(filename, outfile=None, outdir=None, mass=1.00):
    """Load a raw data file and save it as cellpy-file.

    Args:
        mass (float): active material mass [mg].
        outdir (path): optional, path to directory for saving the hdf5-file.
        outfile (str): optional, name of hdf5-file.
        filename (str): name of the resfile.

    Returns:
        out_file_name (str): name of saved file.
    """
    d = CellpyData()

    if not outdir:
        outdir = prms.Paths["cellpydatadir"]

    if not outfile:
        outfile = os.path.basename(filename).split(".")[0] + ".h5"
        outfile = os.path.join(outdir, outfile)

    print("filename:", filename)
    print("outfile:", outfile)
    print("outdir:", outdir)
    print("mass:", mass, "mg")

    d.from_raw(filename)
    d.set_mass(mass)
    d.make_step_table()
    d.make_summary()
    d.save(filename=outfile)
    d.to_csv(datadir=outdir, cycles=True, raw=True, summary=True)
    return outfile


Instruction:
Here you have a function in Python 3, explain what it does
def load_and_print_resfile(filename, info_dict=None):

    # self.test_no = None
    # self.mass = 1.0  # mass of (active) material (in mg)
    # self.no_cycles = 0.0
    # self.charge_steps = None  # not in use at the moment
    # self.discharge_steps = None  # not in use at the moment
    # self.ir_steps = None  # dict # not in use at the moment
    # self.ocv_steps = None  # dict # not in use at the moment
    # self.nom_cap = 3579  # mAh/g (used for finding c-rates)
    # self.mass_given = False
    # self.c_mode = True
    # self.starts_with = "discharge"
    # self.material = "noname"
    # self.merged = False
    # self.file_errors = None  # not in use at the moment
    # self.loaded_from = None  # name of the .res file it is loaded from
    # (can be list if merged)
    # self.raw_data_files = []
    # self.raw_data_files_length = []
    # # self.parent_filename = None # name of the .res file it is loaded from
    # (basename) (can be list if merded)
    # # self.parent_filename = if listtype, for file in etc,,,
    # os.path.basename(self.loaded_from)
    # self.channel_index = None
    # self.channel_number = None
    # self.creator = None
    # self.item_ID = None
    # self.schedule_file_name = None
    # self.start_datetime = None
    # self.test_ID = None
    # self.name = None

    # NEXT: include nom_cap, tot_mass and  parameters table in save/load hdf5
    if info_dict is None:
        info_dict = dict()
        info_dict["mass"] = 1.23  # mg
        info_dict["nom_cap"] = 3600  # mAh/g (active material)
        info_dict["tot_mass"] = 2.33  # mAh/g (total mass of material)

    d = CellpyData()

    print("filename:", filename)
    print("info_dict in:", end=' ')
    print(info_dict)

    d.from_raw(filename)
    d.set_mass(info_dict["mass"])
    d.make_step_table()
    d.make_summary()

    for test in d.datasets:
        print("newtest")
        print(test)

    return info_dict

Response: Loads a raw data file and prints information.


Instruction:
How would you implement a function in Python 3 that
sets the instrument used for obtaining the data.

Response:
def set_instrument(self, instrument=None):
        """Set the instrument (i.e. tell cellpy the file-type you use).

        Args:
            instrument: (str) in ["arbin", "bio-logic-csv", "bio-logic-bin",...]

        Sets the instrument used for obtaining the data (i.e. sets fileformat)

        """
        if instrument is None:
            instrument = self.tester

        if instrument in ["arbin", "arbin_res"]:
            self._set_arbin()
            self.tester = "arbin"

        elif instrument == "arbin_sql":
            self._set_arbin_sql()
            self.tester = "arbin"

        elif instrument == "arbin_experimental":
            self._set_arbin_experimental()
            self.tester = "arbin"

        elif instrument in ["pec", "pec_csv"]:
            self._set_pec()
            self.tester = "pec"

        elif instrument in ["biologics", "biologics_mpr"]:
            self._set_biologic()
            self.tester = "biologic"

        elif instrument == "custom":
            self._set_custom()
            self.tester = "custom"

        else:
            raise Exception(f"option does not exist: '{instrument}'")


Instruction:
Can you tell what is the following Python 3 function doing
def set_raw_datadir(self, directory=None):

        if directory is None:
            self.logger.info("no directory name given")
            return
        if not os.path.isdir(directory):
            self.logger.info(directory)
            self.logger.info("directory does not exist")
            return
        self.raw_datadir = directory

Response: Set the directory containing. res - files.


Instruction:
Can you implement a function in Python 3 that
sets the directory containing. hdf5 - files.

Response:
def set_cellpy_datadir(self, directory=None):
        """Set the directory containing .hdf5-files.

        Used for setting directory for looking for hdf5-files.
        A valid directory name is required.

        Args:
            directory (str): path to hdf5-directory

        Example:
            >>> d = CellpyData()
            >>> directory = "MyData/HDF5"
            >>> d.set_raw_datadir(directory)

        """

        if directory is None:
            self.logger.info("no directory name given")
            return
        if not os.path.isdir(directory):
            self.logger.info("directory does not exist")
            return
        self.cellpy_datadir = directory


Instruction:
Write a Python 3 function for
checking the stats for the files in the raw - data and cellpy hdf5.

Response:
def check_file_ids(self, rawfiles, cellpyfile):
        """Check the stats for the files (raw-data and cellpy hdf5).

        This function checks if the hdf5 file and the res-files have the same
        timestamps etc to find out if we need to bother to load .res -files.

        Args:
            cellpyfile (str): filename of the cellpy hdf5-file.
            rawfiles (list of str): name(s) of raw-data file(s).

        Returns:
            False if the raw files are newer than the cellpy hdf5-file
                (update needed).
            If return_res is True it also returns list of raw-file_names as
                second argument.
            """

        txt = "checking file ids - using '%s'" % self.filestatuschecker
        self.logger.info(txt)

        ids_cellpy_file = self._check_cellpy_file(cellpyfile)

        self.logger.debug(f"cellpyfile ids: {ids_cellpy_file}")

        if not ids_cellpy_file:
            # self.logger.debug("hdf5 file does not exist - needs updating")
            return False

        ids_raw = self._check_raw(rawfiles)
        similar = self._compare_ids(ids_raw, ids_cellpy_file)

        if not similar:
            # self.logger.debug("hdf5 file needs updating")
            return False
        else:
            # self.logger.debug("hdf5 file is updated")
            return True


Instruction:
Write a Python 3 function that can
get the file - ids for the res_files.

Response:
def _check_raw(self, file_names, abort_on_missing=False):
        """Get the file-ids for the res_files."""

        strip_file_names = True
        check_on = self.filestatuschecker
        if not self._is_listtype(file_names):
            file_names = [file_names, ]

        ids = dict()
        for f in file_names:
            self.logger.debug(f"checking res file {f}")
            fid = FileID(f)
            # self.logger.debug(fid)
            if fid.name is None:
                warnings.warn(f"file does not exist: {f}")
                if abort_on_missing:
                    sys.exit(-1)
            else:
                if strip_file_names:
                    name = os.path.basename(f)
                else:
                    name = f
                if check_on == "size":
                    ids[name] = int(fid.size)
                elif check_on == "modified":
                    ids[name] = int(fid.last_modified)
                else:
                    ids[name] = int(fid.last_accessed)
        return ids


Instruction:
Implement a function in Python 3 to
check the file - ids for the cellpy - file.

Response:
def _check_cellpy_file(self, filename):
        """Get the file-ids for the cellpy_file."""

        strip_filenames = True
        check_on = self.filestatuschecker
        self.logger.debug("checking cellpy-file")
        self.logger.debug(filename)
        if not os.path.isfile(filename):
            self.logger.debug("cellpy-file does not exist")
            return None
        try:
            store = pd.HDFStore(filename)
        except Exception as e:
            self.logger.debug(f"could not open cellpy-file ({e})")
            return None
        try:
            fidtable = store.select("CellpyData/fidtable")
        except KeyError:
            self.logger.warning("no fidtable -"
                                " you should update your hdf5-file")
            fidtable = None
        finally:
            store.close()
        if fidtable is not None:
            raw_data_files, raw_data_files_length = \
                self._convert2fid_list(fidtable)
            txt = "contains %i res-files" % (len(raw_data_files))
            self.logger.debug(txt)
            ids = dict()
            for fid in raw_data_files:
                full_name = fid.full_name
                size = fid.size
                mod = fid.last_modified
                self.logger.debug(f"fileID information for: {full_name}")
                self.logger.debug(f"   modified: {mod}")
                self.logger.debug(f"   size: {size}")

                if strip_filenames:
                    name = os.path.basename(full_name)
                else:
                    name = full_name
                if check_on == "size":
                    ids[name] = int(fid.size)
                elif check_on == "modified":
                    ids[name] = int(fid.last_modified)
                else:
                    ids[name] = int(fid.last_accessed)
            return ids
        else:
            return None


Instruction:
Can you tell what is the following Python 3 function doing
def loadcell(self, raw_files, cellpy_file=None, mass=None,
                 summary_on_raw=False, summary_ir=True, summary_ocv=False,
                 summary_end_v=True, only_summary=False, only_first=False,
                 force_raw=False,
                 use_cellpy_stat_file=None):

        """Loads data for given cells.

        Args:
            raw_files (list): name of res-files
            cellpy_file (path): name of cellpy-file
            mass (float): mass of electrode or active material
            summary_on_raw (bool): use raw-file for summary
            summary_ir (bool): summarize ir
            summary_ocv (bool): summarize ocv steps
            summary_end_v (bool): summarize end voltage
            only_summary (bool): get only the summary of the runs
            only_first (bool): only use the first file fitting search criteria
            force_raw (bool): only use raw-files
            use_cellpy_stat_file (bool): use stat file if creating summary
                from raw

        Example:

            >>> srnos = my_dbreader.select_batch("testing_new_solvent")
            >>> cell_datas = []
            >>> for srno in srnos:
            >>> ... my_run_name = my_dbreader.get_cell_name(srno)
            >>> ... mass = my_dbreader.get_mass(srno)
            >>> ... rawfiles, cellpyfiles = \
            >>> ...     filefinder.search_for_files(my_run_name)
            >>> ... cell_data = cellreader.CellpyData()
            >>> ... cell_data.loadcell(raw_files=rawfiles,
            >>> ...                    cellpy_file=cellpyfiles)
            >>> ... cell_data.set_mass(mass)
            >>> ... if not cell_data.summary_exists:
            >>> ...     cell_data.make_summary() # etc. etc.
            >>> ... cell_datas.append(cell_data)
            >>>
        """

        # This is a part of a dramatic API change. It will not be possible to
        # load more than one set of datasets (i.e. one single cellpy-file or
        # several raw-files that will be automatically merged)
        self.logger.info("started loadcell")
        if cellpy_file is None:
            similar = False
        elif force_raw:
            similar = False
        else:
            similar = self.check_file_ids(raw_files, cellpy_file)
        self.logger.debug("checked if the files were similar")

        if only_summary:
            self.load_only_summary = True
        else:
            self.load_only_summary = False

        if not similar:
            self.logger.info("cellpy file(s) needs updating - loading raw")
            self.logger.debug(raw_files)
            self.from_raw(raw_files)
            self.logger.debug("loaded files")
            # Check if the run was loaded ([] if empty)
            if self.status_datasets:
                if mass:
                    self.set_mass(mass)
                if summary_on_raw:
                    self.make_summary(all_tests=False, find_ocv=summary_ocv,
                                      find_ir=summary_ir,
                                      find_end_voltage=summary_end_v,
                                      use_cellpy_stat_file=use_cellpy_stat_file)
            else:
                self.logger.warning("Empty run!")

        else:
            self.load(cellpy_file)
        return self

Response: Loads data for given cells.


Instruction:
How would you explain what the following Python 3 function does
def from_raw(self, file_names=None, **kwargs):
        # This function only loads one test at a time (but could contain several
        # files). The function from_res() also implements loading several
        # datasets (using list of lists as input).

        if file_names:
            self.file_names = file_names

        if not isinstance(file_names, (list, tuple)):
            self.file_names = [file_names, ]

        # file_type = self.tester
        raw_file_loader = self.loader
        set_number = 0
        test = None
        counter = 0
        self.logger.debug("start iterating through file(s)")
        for f in self.file_names:
            self.logger.debug("loading raw file:")
            self.logger.debug(f"{f}")
            new_tests = raw_file_loader(f, **kwargs)
            if new_tests:
                if test is not None:
                    self.logger.debug("continuing reading files...")
                    _test = self._append(test[set_number], new_tests[set_number])
                    if not _test:
                        self.logger.warning(f"EMPTY TEST: {f}")
                        continue
                    test[set_number] = _test
                    self.logger.debug("added this test - started merging")
                    for j in range(len(new_tests[set_number].raw_data_files)):
                        raw_data_file = new_tests[set_number].raw_data_files[j]
                        file_size = new_tests[set_number].raw_data_files_length[j]
                        test[set_number].raw_data_files.append(raw_data_file)
                        test[set_number].raw_data_files_length.append(file_size)
                        counter += 1
                        if counter > 10:
                            self.logger.debug("ERROR? Too many files to merge")
                            raise ValueError("Too many files to merge - "
                                             "could be a p2-p3 zip thing")
                else:
                    self.logger.debug("getting data from first file")
                    if new_tests[set_number].no_data:
                        self.logger.debug("NO DATA")
                    else:
                        test = new_tests
            else:
                self.logger.debug("NOTHING LOADED")

        self.logger.debug("finished loading the raw-files")

        test_exists = False
        if test:
            if test[0].no_data:
                self.logging.debug("the first dataset (or only dataset) loaded from the raw data file is empty")
            else:
                test_exists = True

        if test_exists:
            if not prms.Reader.sorted_data:
                self.logger.debug("sorting data")
                test[set_number] = self._sort_data(test[set_number])

            self.datasets.append(test[set_number])
        else:
            self.logger.warning("No new datasets added!")
        self.number_of_datasets = len(self.datasets)
        self.status_datasets = self._validate_datasets()
        self._invent_a_name()
        return self

Response: Load a single raw - file into the current object.


Instruction:
Write a Python 3 script for
returning True if datasets exists and if one or more of the datasets are empty.

Response:
def check(self):
        """Returns False if no datasets exists or if one or more of the datasets
        are empty"""

        if len(self.status_datasets) == 0:
            return False
        if all(self.status_datasets):
            return True
        return False


Instruction:
Explain what the following Python 3 code does
def load(self, cellpy_file, parent_level="CellpyData"):

        try:
            self.logger.debug("loading cellpy-file (hdf5):")
            self.logger.debug(cellpy_file)
            new_datasets = self._load_hdf5(cellpy_file, parent_level)
            self.logger.debug("cellpy-file loaded")
        except AttributeError:
            new_datasets = []
            self.logger.warning("This cellpy-file version is not supported by"
                                "current reader (try to update cellpy).")

        if new_datasets:
            for dataset in new_datasets:
                self.datasets.append(dataset)
        else:
            # raise LoadError
            self.logger.warning("Could not load")
            self.logger.warning(str(cellpy_file))

        self.number_of_datasets = len(self.datasets)
        self.status_datasets = self._validate_datasets()
        self._invent_a_name(cellpy_file)
        return self

Response: Loads a cellpy file into the internal state.


Instruction:
Can you create a Python 3 function that
loads a cellpy - file into a set of objects.

Response:
def _load_hdf5(self, filename, parent_level="CellpyData"):
        """Load a cellpy-file.

        Args:
            filename (str): Name of the cellpy file.
            parent_level (str) (optional): name of the parent level
                (defaults to "CellpyData")

        Returns:
            loaded datasets (DataSet-object)
        """

        if not os.path.isfile(filename):
            self.logger.info(f"file does not exist: {filename}")
            raise IOError
        store = pd.HDFStore(filename)

        # required_keys = ['dfdata', 'dfsummary', 'fidtable', 'info']
        required_keys = ['dfdata', 'dfsummary', 'info']
        required_keys = ["/" + parent_level + "/" + _ for _ in required_keys]

        for key in required_keys:
            if key not in store.keys():
                self.logger.info(f"This hdf-file is not good enough - "
                                 f"at least one key is missing: {key}")
                raise Exception(f"OH MY GOD! At least one crucial key"
                                f"is missing {key}!")

        self.logger.debug(f"Keys in current hdf5-file: {store.keys()}")
        data = DataSet()

        if parent_level != "CellpyData":
            self.logger.debug("Using non-default parent label for the "
                              "hdf-store: {}".format(parent_level))

        # checking file version
        infotable = store.select(parent_level + "/info")
        try:
            data.cellpy_file_version = \
                self._extract_from_dict(infotable, "cellpy_file_version")
        except Exception as e:
            data.cellpy_file_version = 0
            warnings.warn(f"Unhandled exception raised: {e}")

        if data.cellpy_file_version < MINIMUM_CELLPY_FILE_VERSION:
            raise WrongFileVersion

        if data.cellpy_file_version > CELLPY_FILE_VERSION:
            raise WrongFileVersion

        data.dfsummary = store.select(parent_level + "/dfsummary")
        data.dfdata = store.select(parent_level + "/dfdata")

        try:
            data.step_table = store.select(parent_level + "/step_table")
        except Exception as e:
            self.logging.debug("could not get step_table from cellpy-file")
            data.step_table = pd.DataFrame()
            warnings.warn(f"Unhandled exception raised: {e}")

        try:
            fidtable = store.select(
                parent_level + "/fidtable")  # remark! changed spelling from
            # lower letter to camel-case!
            fidtable_selected = True
        except Exception as e:
            self.logging.debug("could not get fid-table from cellpy-file")
            fidtable = []

            warnings.warn("no fidtable - you should update your hdf5-file")
            fidtable_selected = False
        self.logger.debug("  h5")
        # this does not yet allow multiple sets

        newtests = []  # but this is ready when that time comes

        # The infotable stores "meta-data". The follwing statements loads the
        # content of infotable and updates div. DataSet attributes.
        # Maybe better use it as dict?

        data = self._load_infotable(data, infotable, filename)

        if fidtable_selected:
            data.raw_data_files, data.raw_data_files_length = \
                self._convert2fid_list(fidtable)
        else:
            data.raw_data_files = None
            data.raw_data_files_length = None
        newtests.append(data)
        store.close()
        # self.datasets.append(data)
        return newtests


Instruction:
Given the following Python 3 function, write the documentation
def merge(self, datasets=None, separate_datasets=False):
        self.logger.info("merging")
        if separate_datasets:
            warnings.warn("The option seperate_datasets=True is"
                          "not implemented yet. Performing merging, but"
                          "neglecting the option.")
        else:
            if datasets is None:
                datasets = list(range(len(self.datasets)))
            first = True
            for dataset_number in datasets:
                if first:
                    dataset = self.datasets[dataset_number]
                    first = False
                else:
                    dataset = self._append(dataset, self.datasets[dataset_number])
                    for raw_data_file, file_size in zip(self.datasets[dataset_number].raw_data_files,
                                                        self.datasets[dataset_number].raw_data_files_length):
                        dataset.raw_data_files.append(raw_data_file)
                        dataset.raw_data_files_length.append(file_size)
            self.datasets = [dataset]
            self.number_of_datasets = 1
        return self

Response: This function merges datasets into one set.


Instruction:
Can you generate the documentation for the following Python 3 function
def print_step_table(self, dataset_number=None):
        dataset_number = self._validate_dataset_number(dataset_number)
        if dataset_number is None:
            self._report_empty_dataset()
            return
        st = self.datasets[dataset_number].step_table
        print(st)

Response: Print the step table.


Instruction:
Make a summary of the following Python 3 code
def get_step_numbers(self, steptype='charge', allctypes=True, pdtype=False,
                         cycle_number=None, dataset_number=None,
                         steptable=None):
        # TODO: @jepe - include sub_steps here
        """Get the step numbers of selected type.

        Returns the selected step_numbers for the  elected type of step(s).

        Args:
            steptype (string): string identifying type of step.
            allctypes (bool): get all types of charge (or discharge).
            pdtype (bool): return results as pandas.DataFrame
            cycle_number (int): selected cycle, selects all if not set.
            dataset_number (int): test number (default first)
                (usually not used).
            steptable (pandas.DataFrame): optional steptable

        Returns:
            A dictionary containing a list of step numbers corresponding
                to the selected steptype for the cycle(s).
            Returns a pandas.DataFrame instead of a dict of lists if pdtype is
                set to True. The frame is a sub-set of the step-table frame
                (i.e. all the same columns, only filtered by rows).

        Example:
            >>> my_charge_steps = CellpyData.get_step_numbers(
            >>>    "charge",
            >>>    cycle_number = 3
            >>> )
            >>> print my_charge_steps
            {3: [5,8]}

        """
        # self.logger.debug("Trying to get step-types")
        if steptable is None:
            dataset_number = self._validate_dataset_number(dataset_number)
            if dataset_number is None:
                self._report_empty_dataset()
                return

            if not self.datasets[dataset_number].step_table_made:
                self.logger.debug("step_table is not made")

                if self.force_step_table_creation or self.force_all:
                    self.logger.debug("creating step_table for")
                    self.logger.debug(self.datasets[dataset_number].loaded_from)
                    # print "CREAING STEP-TABLE"
                    self.make_step_table(dataset_number=dataset_number)

                else:
                    self.logger.info("ERROR! Cannot use get_steps: "
                                     "create step_table first")
                    self.logger.info(" you could use find_step_numbers"
                                     " method instead")
                    self.logger.info(" (but I don't recommend it)")
                    return None

        # check if steptype is valid
        steptype = steptype.lower()
        steptypes = []
        helper_step_types = ['ocv', 'charge_discharge']
        valid_step_type = True
        if steptype in self.list_of_step_types:
            steptypes.append(steptype)
        else:
            txt = "%s is not a valid core steptype" % steptype
            if steptype in helper_step_types:
                txt = "but a helper steptype"
                if steptype == 'ocv':
                    steptypes.append('ocvrlx_up')
                    steptypes.append('ocvrlx_down')
                elif steptype == 'charge_discharge':
                    steptypes.append('charge')
                    steptypes.append('discharge')
            else:
                valid_step_type = False
            self.logger.debug(txt)
        if not valid_step_type:
            return None

        # in case of selection allctypes, then modify charge, discharge
        if allctypes:
            add_these = []
            for st in steptypes:
                if st in ['charge', 'discharge']:
                    st1 = st + '_cv'
                    add_these.append(st1)
                    st1 = 'cv_' + st
                    add_these.append(st1)
            for st in add_these:
                steptypes.append(st)

        # self.logger.debug("Your steptypes:")
        # self.logger.debug(steptypes)

        if steptable is None:
            st = self.datasets[dataset_number].step_table
        else:
            st = steptable
        shdr = self.headers_step_table

        # retrieving cycle numbers
        if cycle_number is None:
            cycle_numbers = self.get_cycle_numbers(
                dataset_number,
                steptable=steptable
            )

        else:
            if isinstance(cycle_number, (list, tuple)):
                cycle_numbers = cycle_number
            else:
                cycle_numbers = [cycle_number, ]

        if pdtype:
            self.logger.debug("return pandas dataframe")
            out = st[st[shdr.type].isin(steptypes) &
                     st[shdr.cycle].isin(cycle_numbers)]
            return out

        # if not pdtype, return a dict instead
        # self.logger.debug("out as dict; out[cycle] = [s1,s2,...]")
        # self.logger.debug("(same behaviour as find_step_numbers)")
        self.logger.debug("return dict of lists")
        out = dict()
        for cycle in cycle_numbers:
            steplist = []
            for s in steptypes:
                step = st[(st[shdr.type] == s) &
                          (st[shdr.cycle] == cycle)][shdr.step].tolist()
                for newstep in step:
                    steplist.append(int(newstep))
                    # int(step.iloc[0])
                    # self.is_empty(steps)
            if not steplist:
                steplist = [0]
            out[cycle] = steplist
        return out

Response: Get the step numbers of a given type of step.


Instruction:
How would you explain what the following Python 3 function does
def load_step_specifications(self, file_name, short=False,
                                 dataset_number=None):
        """ Load a table that contains step-type definitions.

        This function loads a file containing a specification for each step or
        for each (cycle_number, step_number) combinations if short==False. The
        step_cycle specifications that are allowed are stored in the variable
        cellreader.list_of_step_types.
        """

        dataset_number = self._validate_dataset_number(dataset_number)
        if dataset_number is None:
            self._report_empty_dataset()
            return

        # if short:
        #     # the table only consists of steps (not cycle,step pairs) assuming
        #     # that the step numbers uniquely defines step type (this is true
        #     # for arbin at least).
        #     raise NotImplementedError

        step_specs = pd.read_csv(file_name, sep=prms.Reader.sep)
        if "step" not in step_specs.columns:
            self.logger.info("step col is missing")
            raise IOError

        if "type" not in step_specs.columns:
            self.logger.info("type col is missing")
            raise IOError

        if not short and "cycle" not in step_specs.columns:
            self.logger.info("cycle col is missing")
            raise IOError

        self.make_step_table(custom_step_definition=True,
                             step_specifications=step_specs,
                             short=short)

Response: Load a table containing step - type definitions.


Instruction:
How would you implement a function in Python 3 that
creates a table that contains summary information for each step.

Response:
def make_step_table(self, custom_step_definition=False,
                        step_specifications=None,
                        short=False,
                        dataset_number=None):

        """ Create a table (v.4) that contains summary information for each step.

        This function creates a table containing information about the
        different steps for each cycle and, based on that, decides what type of
        step it is (e.g. charge) for each cycle.

        The format of the step_table is:

            index: cycleno - stepno - sub-step-no
            Logging info (average, stdev, max, min, start, end, delta) -
            Current info (average, stdev, max, min, start, end, delta) -
            Voltage info (average,  stdev, max, min, start, end, delta) -
            Type (from pre-defined list) - SubType -
            Info
        """
        time_00 = time.time()
        dataset_number = self._validate_dataset_number(dataset_number)
        if dataset_number is None:
            self._report_empty_dataset()
            return

        nhdr = self.headers_normal
        shdr = self.headers_step_table

        df = self.datasets[dataset_number].dfdata
        # df[shdr.internal_resistance_change] = \
        #     df[nhdr.internal_resistance_txt].pct_change()

        def first(x):
            return x.iloc[0]

        def last(x):
            return x.iloc[-1]

        def delta(x):
            if x.iloc[0] == 0.0:
                # starts from a zero value
                difference = 100.0 * x.iloc[-1]
            else:
                difference = (x.iloc[-1] - x.iloc[0]) * 100 / x.iloc[0]

            return difference

        keep = [
            nhdr.data_point_txt,
            nhdr.step_time_txt,
            nhdr.step_index_txt,
            nhdr.cycle_index_txt,
            nhdr.current_txt,
            nhdr.voltage_txt,
            nhdr.ref_voltage_txt,
            nhdr.charge_capacity_txt,
            nhdr.discharge_capacity_txt,
            nhdr.internal_resistance_txt,
            # "ir_pct_change"
        ]

        # only use col-names that exist:
        keep = [col for col in keep if col in df.columns]

        df = df[keep]
        df[nhdr.sub_step_index_txt] = 1
        rename_dict = {
            nhdr.cycle_index_txt: shdr.cycle,
            nhdr.step_index_txt: shdr.step,
            nhdr.sub_step_index_txt: shdr.sub_step,
            nhdr.data_point_txt: shdr.point,
            nhdr.step_time_txt: shdr.step_time,
            nhdr.current_txt: shdr.current,
            nhdr.voltage_txt: shdr.voltage,
            nhdr.charge_capacity_txt: shdr.charge,
            nhdr.discharge_capacity_txt: shdr.discharge,
            nhdr.internal_resistance_txt: shdr.internal_resistance,
        }

        df = df.rename(columns=rename_dict)

        by = [shdr.cycle, shdr.step, shdr.sub_step]

        self.logger.debug(f"groupby: {by}")

        gf = df.groupby(by=by)
        df_steps = (gf.agg(
            [np.mean, np.std, np.amin, np.amax, first, last, delta]
        ).rename(columns={'amin': 'min', 'amax': 'max', 'mean': 'avr'}))

        df_steps = df_steps.reset_index()

        df_steps[shdr.type] = np.nan
        df_steps[shdr.sub_type] = np.nan
        df_steps[shdr.info] = np.nan

        current_limit_value_hard = self.raw_limits["current_hard"]
        current_limit_value_soft = self.raw_limits["current_soft"]
        stable_current_limit_hard = self.raw_limits["stable_current_hard"]
        stable_current_limit_soft = self.raw_limits["stable_current_soft"]
        stable_voltage_limit_hard = self.raw_limits["stable_voltage_hard"]
        stable_voltage_limit_soft = self.raw_limits["stable_voltage_soft"]
        stable_charge_limit_hard = self.raw_limits["stable_charge_hard"]
        stable_charge_limit_soft = self.raw_limits["stable_charge_soft"]
        ir_change_limit = self.raw_limits["ir_change"]

        mask_no_current_hard = (
            df_steps.loc[:, (shdr.current, "max")].abs()
            + df_steps.loc[:, (shdr.current, "min")].abs()
        ) < current_limit_value_hard

        mask_voltage_down = df_steps.loc[:, (shdr.voltage, "delta")] < \
            - stable_voltage_limit_hard

        mask_voltage_up = df_steps.loc[:, (shdr.voltage, "delta")] > \
            stable_voltage_limit_hard

        mask_voltage_stable = df_steps.loc[:, (shdr.voltage, "delta")].abs() < \
            stable_voltage_limit_hard

        mask_current_down = df_steps.loc[:, (shdr.current, "delta")] < \
            - stable_current_limit_soft

        mask_current_up = df_steps.loc[:, (shdr.current, "delta")] > \
            stable_current_limit_soft

        mask_current_negative = df_steps.loc[:, (shdr.current, "avr")] < \
            - current_limit_value_hard

        mask_current_positive = df_steps.loc[:, (shdr.current, "avr")] > \
            current_limit_value_hard

        mask_galvanostatic = df_steps.loc[:, (shdr.current, "delta")].abs() < \
            stable_current_limit_soft

        mask_charge_changed = df_steps.loc[:, (shdr.charge, "delta")].abs() > \
            stable_charge_limit_hard

        mask_discharge_changed = df_steps.loc[:, (shdr.discharge, "delta")].abs() > \
            stable_charge_limit_hard

        mask_no_change = (df_steps.loc[:, (shdr.voltage, "delta")] == 0) & \
            (df_steps.loc[:, (shdr.current, "delta")] == 0) & \
            (df_steps.loc[:, (shdr.charge, "delta")] == 0) & \
            (df_steps.loc[:, (shdr.charge, "delta")] == 0)

        if custom_step_definition:
            self.logger.debug("parsing custom step definition")
            if not short:
                self.logger.debug("using long format (cycle,step)")
                for row in step_specifications.itertuples():
                    # self.logger.debug(f"cycle: {row.cycle} step: {row.step}"
                    #                   f" type: {row.type}")
                    df_steps.loc[(df_steps[shdr.step] == row.step) &
                                 (df_steps[shdr.cycle] == row.cycle),
                                 "type"] = row.type
                    df_steps.loc[(df_steps[shdr.step] == row.step) &
                                 (df_steps[shdr.cycle] == row.cycle),
                                 "info"] = row.info
            else:
                self.logger.debug("using short format (step)")
                for row in step_specifications.itertuples():
                    # self.logger.debug(f"step: {row.step} "
                    #                   f"type: {row.type}"
                    #                   f"info: {row.info}")

                    df_steps.loc[df_steps[shdr.step] == row.step,
                                 "type"] = row.type
                    df_steps.loc[df_steps[shdr.step] == row.step,
                                 "info"] = row.info

        else:
            self.logger.debug("masking and labelling steps")
            df_steps.loc[mask_no_current_hard & mask_voltage_stable,
                         shdr.type] = 'rest'

            df_steps.loc[mask_no_current_hard & mask_voltage_up,
                         shdr.type] = 'ocvrlx_up'

            df_steps.loc[mask_no_current_hard & mask_voltage_down,
                         shdr.type] = 'ocvrlx_down'

            df_steps.loc[mask_discharge_changed & mask_current_negative,
                         shdr.type] = 'discharge'

            df_steps.loc[mask_charge_changed & mask_current_positive,
                         shdr.type] = 'charge'

            df_steps.loc[
                mask_voltage_stable & mask_current_negative & mask_current_down,
                shdr.type
            ] = 'cv_discharge'

            df_steps.loc[mask_voltage_stable & mask_current_positive &
                         mask_current_down, shdr.type] = 'cv_charge'

            # --- internal resistance ----
            df_steps.loc[mask_no_change, shdr.type] = 'ir'
            # assumes that IR is stored in just one row

            # --- sub-step-txt -----------
            df_steps[shdr.sub_type] = None

            # --- CV steps ----

            # "voltametry_charge"
            # mask_charge_changed
            # mask_voltage_up
            # (could also include abs-delta-cumsum current)

            # "voltametry_discharge"
            # mask_discharge_changed
            # mask_voltage_down

        # check if all the steps got categorizes
        self.logger.debug("looking for un-categorized steps")
        empty_rows = df_steps.loc[df_steps[shdr.type].isnull()]
        if not empty_rows.empty:
            logging.warning(
                f"found {len(empty_rows)}"
                f":{len(df_steps)} non-categorized steps "
                f"(please, check your raw-limits)")

        # flatten (possible remove in the future),
        # (maybe we will implement mulitindexed tables)

        self.logger.debug(f"flatten columns")
        flat_cols = []
        for col in df_steps.columns:
            if isinstance(col, tuple):
                if col[-1]:
                    col = "_".join(col)
                else:
                    col = col[0]
            flat_cols.append(col)

        df_steps.columns = flat_cols

        self.datasets[dataset_number].step_table = df_steps
        self.logger.debug(f"(dt: {(time.time() - time_00):4.2f}s)")
        return self


Instruction:
Can you generate the documentation for the following Python 3 function
def to_csv(self, datadir=None, sep=None, cycles=False, raw=True,
               summary=True, shifted=False,
               method=None, shift=0.0,
               last_cycle=None):
        """Saves the data as .csv file(s).

        Args:
            datadir: folder where to save the data (uses current folder if not
                given).
            sep: the separator to use in the csv file
                (defaults to CellpyData.sep).
            cycles: (bool) export voltage-capacity curves if True.
            raw: (bool) export raw-data if True.
            summary: (bool) export summary if True.
            shifted (bool): export with cumulated shift.
            method (string): how the curves are given
                "back-and-forth" - standard back and forth; discharge
                    (or charge) reversed from where charge (or
                    discharge) ends.
                "forth" - discharge (or charge) continues along x-axis.
                "forth-and-forth" - discharge (or charge) also starts at 0 (or
                    shift if not shift=0.0)
            shift: start-value for charge (or discharge)
            last_cycle: process only up to this cycle (if not None).

        Returns: Nothing

        """

        if sep is None:
            sep = self.sep

        self.logger.debug("saving to csv")

        dataset_number = -1
        for data in self.datasets:
            dataset_number += 1
            if not self._is_not_empty_dataset(data):
                self.logger.info("to_csv -")
                self.logger.info("empty test [%i]" % dataset_number)
                self.logger.info("not saved!")
            else:
                if isinstance(data.loaded_from, (list, tuple)):
                    txt = "merged file"
                    txt += "using first file as basename"
                    self.logger.debug(txt)
                    no_merged_sets = len(data.loaded_from)
                    no_merged_sets = "_merged_" + str(no_merged_sets).zfill(3)
                    filename = data.loaded_from[0]
                else:
                    filename = data.loaded_from
                    no_merged_sets = ""
                firstname, extension = os.path.splitext(filename)
                firstname += no_merged_sets
                if datadir:
                    firstname = os.path.join(datadir,
                                             os.path.basename(firstname))

                if raw:
                    outname_normal = firstname + "_normal.csv"
                    self._export_normal(data, outname=outname_normal, sep=sep)
                    if data.step_table_made is True:
                        outname_steps = firstname + "_steps.csv"
                        self._export_steptable(data, outname=outname_steps,
                                               sep=sep)
                    else:
                        self.logger.debug("step_table_made is not True")

                if summary:
                    outname_stats = firstname + "_stats.csv"
                    self._export_stats(data, outname=outname_stats, sep=sep)

                if cycles:
                    outname_cycles = firstname + "_cycles.csv"
                    self._export_cycles(outname=outname_cycles,
                                        dataset_number=dataset_number,
                                        sep=sep, shifted=shifted,
                                        method=method, shift=shift,
                                        last_cycle=last_cycle)

Response: Saves the data in the cellpy data file.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def save(self, filename, dataset_number=None, force=False, overwrite=True,
             extension="h5", ensure_step_table=None):
        """Save the data structure to cellpy-format.

        Args:
            filename: (str) the name you want to give the file
            dataset_number: (int) if you have several datasets, chose the one
                you want (probably leave this untouched)
            force: (bool) save a file even if the summary is not made yet
                (not recommended)
            overwrite: (bool) save the new version of the file even if old one
                exists.
            extension: (str) filename extension.
            ensure_step_table: (bool) make step-table if missing.

        Returns: Nothing at all.
        """
        if ensure_step_table is None:
            ensure_step_table = self.ensure_step_table

        dataset_number = self._validate_dataset_number(dataset_number)
        if dataset_number is None:
            self.logger.info("Saving test failed!")
            self._report_empty_dataset()
            return

        test = self.get_dataset(dataset_number)

        dfsummary_made = test.dfsummary_made

        if not dfsummary_made and not force:
            self.logger.info(
                "You should not save datasets "
                "without making a summary first!"
            )
            self.logger.info(
                "If you really want to do it, "
                "use save with force=True"
            )
            return

        step_table_made = test.step_table_made

        if not step_table_made and not force and not ensure_step_table:
            self.logger.info(
                "You should not save datasets "
                "without making a step-table first!"
            )
            self.logger.info(
                "If you really want to do it, "
                "use save with force=True"
            )
            return

        if not os.path.splitext(filename)[-1]:
            outfile_all = filename + "." + extension
        else:
            outfile_all = filename

        if os.path.isfile(outfile_all):
            self.logger.debug("Outfile exists")
            if overwrite:
                self.logger.debug("overwrite = True")
                os.remove(outfile_all)
            else:
                self.logger.info(
                    "save (hdf5): file exist - did not save",
                    end=' '
                )
                self.logger.info(outfile_all)
                return

        if ensure_step_table:
            self.logger.debug("ensure_step_table is on")
            if not test.step_table_made:
                self.logger.debug("save: creating step table")
                self.make_step_table(dataset_number=dataset_number)

        # This method can probalby be updated using pandas transpose trick
        self.logger.debug("trying to make infotable")
        infotbl, fidtbl = self._create_infotable(
            dataset_number=dataset_number
        )
        root = prms._cellpyfile_root

        self.logger.debug("trying to save to hdf5")
        txt = "\nHDF5 file: %s" % outfile_all
        self.logger.debug(txt)

        warnings.simplefilter("ignore", PerformanceWarning)
        try:
            store = pd.HDFStore(
                outfile_all,
                complib=prms._cellpyfile_complib,
                complevel=prms._cellpyfile_complevel,
            )

            self.logger.debug("trying to put dfdata")

            self.logger.debug(" - lets set Data_Point as index")
            hdr_data_point = self.headers_normal.data_point_txt
            test.dfdata = test.dfdata.set_index(hdr_data_point,
                                                drop=False)

            store.put(root + "/dfdata", test.dfdata,
                      format=prms._cellpyfile_dfdata_format)
            self.logger.debug(" dfdata -> hdf5 OK")

            self.logger.debug("trying to put dfsummary")
            store.put(root + "/dfsummary", test.dfsummary,
                      format=prms._cellpyfile_dfsummary_format)
            self.logger.debug(" dfsummary -> hdf5 OK")

            self.logger.debug("trying to put infotbl")
            store.put(root + "/info", infotbl,
                      format=prms._cellpyfile_infotable_format)
            self.logger.debug(" infotable -> hdf5 OK")

            self.logger.debug("trying to put fidtable")
            store.put(root + "/fidtable", fidtbl,
                      format=prms._cellpyfile_fidtable_format)
            self.logger.debug(" fidtable -> hdf5 OK")

            self.logger.debug("trying to put step_table")
            try:
                store.put(root + "/step_table", test.step_table,
                          format=prms._cellpyfile_stepdata_format)
                self.logger.debug(" step_table -> hdf5 OK")
            except TypeError:
                test = self._fix_dtype_step_table(test)
                store.put(root + "/step_table", test.step_table,
                          format=prms._cellpyfile_stepdata_format)
                self.logger.debug(" fixed step_table -> hdf5 OK")

            # creating indexes
            # hdr_data_point = self.headers_normal.data_point_txt
            # hdr_cycle_steptable = self.headers_step_table.cycle
            # hdr_cycle_normal = self.headers_normal.cycle_index_txt

            # store.create_table_index(root + "/dfdata", columns=[hdr_data_point],
            #                          optlevel=9, kind='full')
        finally:
            store.close()
        self.logger.debug(" all -> hdf5 OK")
        warnings.simplefilter("default", PerformanceWarning)

Response: Save the data structure to a file.


Instruction:
Can you generate the documentation for the following Python 3 function
def sget_voltage(self, cycle, step, set_number=None):

        time_00 = time.time()
        set_number = self._validate_dataset_number(set_number)
        if set_number is None:
            self._report_empty_dataset()
            return
        cycle_index_header = self.headers_normal.cycle_index_txt
        voltage_header = self.headers_normal.voltage_txt
        step_index_header = self.headers_normal.step_index_txt
        test = self.datasets[set_number].dfdata

        if isinstance(step, (list, tuple)):
            warnings.warn(f"The varialbe step is a list."
                          f"Should be an integer."
                          f"{step}")
            step = step[0]

        c = test[(test[cycle_index_header] == cycle) &
                 (test[step_index_header] == step)]

        self.logger.debug(f"(dt: {(time.time() - time_00):4.2f}s)")
        if not self.is_empty(c):
            v = c[voltage_header]
            return v
        else:
            return None

Response: Returns the voltage for a cycle step.


Instruction:
Can you generate the documentation for the following Python 3 function
def get_voltage(self, cycle=None, dataset_number=None, full=True):

        dataset_number = self._validate_dataset_number(dataset_number)
        if dataset_number is None:
            self._report_empty_dataset()
            return
        cycle_index_header = self.headers_normal.cycle_index_txt
        voltage_header = self.headers_normal.voltage_txt
        # step_index_header  = self.headers_normal.step_index_txt

        test = self.datasets[dataset_number].dfdata
        if cycle:
            self.logger.debug("getting voltage curve for cycle")
            c = test[(test[cycle_index_header] == cycle)]
            if not self.is_empty(c):
                v = c[voltage_header]
                return v
        else:
            if not full:
                self.logger.debug(
                    "getting list of voltage-curves for all cycles"
                )
                v = []
                no_cycles = np.amax(test[cycle_index_header])
                for j in range(1, no_cycles + 1):
                    txt = "Cycle  %i:  " % j
                    self.logger.debug(txt)
                    c = test[(test[cycle_index_header] == j)]
                    v.append(c[voltage_header])
            else:
                self.logger.debug("getting frame of all voltage-curves")
                v = test[voltage_header]
            return v

Response: Returns the voltage of the current cycle.


Instruction:
Can you implement a function in Python 3 that
returns the current in mA.

Response:
def get_current(self, cycle=None, dataset_number=None, full=True):
        """Returns current (in mA).

        Args:
            cycle: cycle number (all cycles if None)
            dataset_number: first dataset if None
            full: valid only for cycle=None (i.e. all cycles), returns the full
               pandas.Series if True, else a list of pandas.Series

        Returns:
            pandas.Series (or list of pandas.Series if cycle=None og full=False)
        """

        dataset_number = self._validate_dataset_number(dataset_number)
        if dataset_number is None:
            self._report_empty_dataset()
            return
        cycle_index_header = self.headers_normal.cycle_index_txt
        current_header = self.headers_normal.current_txt
        # step_index_header  = self.headers_normal.step_index_txt

        test = self.datasets[dataset_number].dfdata
        if cycle:
            self.logger.debug(f"getting current for cycle {cycle}")
            c = test[(test[cycle_index_header] == cycle)]
            if not self.is_empty(c):
                v = c[current_header]
                return v
        else:
            if not full:
                self.logger.debug(
                    "getting a list of current-curves for all cycles"
                )
                v = []
                no_cycles = np.amax(test[cycle_index_header])
                for j in range(1, no_cycles + 1):
                    txt = "Cycle  %i:  " % j
                    self.logger.debug(txt)
                    c = test[(test[cycle_index_header] == j)]
                    v.append(c[current_header])
            else:
                self.logger.debug("getting all current-curves ")
                v = test[current_header]
            return v


Instruction:
Can you generate the documentation for the following Python 3 function
def sget_steptime(self, cycle, step, dataset_number=None):

        dataset_number = self._validate_dataset_number(dataset_number)
        if dataset_number is None:
            self._report_empty_dataset()
            return
        cycle_index_header = self.headers_normal.cycle_index_txt
        step_time_header = self.headers_normal.step_time_txt
        step_index_header = self.headers_normal.step_index_txt
        test = self.datasets[dataset_number].dfdata

        if isinstance(step, (list, tuple)):
            warnings.warn(f"The varialbe step is a list."
                          f"Should be an integer."
                          f"{step}")
            step = step[0]

        c = test.loc[
            (test[cycle_index_header] == cycle) &
            (test[step_index_header] == step), :
        ]

        if not self.is_empty(c):
            t = c[step_time_header]
            return t
        else:
            return None

Response: Returns step time for cycle step.


Instruction:
Can you write a function in Python 3 where it
returns the timestamp for cycle step.

Response:
def sget_timestamp(self, cycle, step, dataset_number=None):
        """Returns timestamp for cycle, step.

        Convinience function; same as issuing
           dfdata[(dfdata[cycle_index_header] == cycle) &
                 (dfdata[step_index_header] == step)][timestamp_header]

        Args:
            cycle: cycle number
            step: step number
            dataset_number: the dataset number (automatic selection if None)

        Returns:
            pandas.Series
        """

        dataset_number = self._validate_dataset_number(dataset_number)
        if dataset_number is None:
            self._report_empty_dataset()
            return
        cycle_index_header = self.headers_normal.cycle_index_txt
        timestamp_header = self.headers_normal.test_time_txt
        step_index_header = self.headers_normal.step_index_txt
        test = self.datasets[dataset_number].dfdata

        if isinstance(step, (list, tuple)):
            warnings.warn(f"The varialbe step is a list."
                          f"Should be an integer."
                          f"{step}")
            step = step[0]

        c = test[(test[cycle_index_header] == cycle) &
                 (test[step_index_header] == step)]
        if not self.is_empty(c):
            t = c[timestamp_header]
            return t
        else:
            return pd.Series()


Instruction:
Can you write a function in Python 3 where it
returns the timestamps in sec or minutes for a given cycle.

Response:
def get_timestamp(self, cycle=None, dataset_number=None,
                      in_minutes=False, full=True):
        """Returns timestamps (in sec or minutes (if in_minutes==True)).

        Args:
            cycle: cycle number (all if None)
            dataset_number: first dataset if None
            in_minutes: return values in minutes instead of seconds if True
            full: valid only for cycle=None (i.e. all cycles), returns the full
               pandas.Series if True, else a list of pandas.Series

        Returns:
            pandas.Series (or list of pandas.Series if cycle=None og full=False)
        """

        dataset_number = self._validate_dataset_number(dataset_number)
        if dataset_number is None:
            self._report_empty_dataset()
            return
        cycle_index_header = self.headers_normal.cycle_index_txt
        timestamp_header = self.headers_normal.test_time_txt

        v = pd.Series()
        test = self.datasets[dataset_number].dfdata
        if cycle:
            c = test[(test[cycle_index_header] == cycle)]
            if not self.is_empty(c):
                v = c[timestamp_header]

        else:
            if not full:
                self.logger.debug("getting timestapm for all cycles")
                v = []
                no_cycles = np.amax(test[cycle_index_header])
                for j in range(1, no_cycles + 1):
                    txt = "Cycle  %i:  " % j
                    self.logger.debug(txt)
                    c = test[(test[cycle_index_header] == j)]
                    v.append(c[timestamp_header])
            else:
                self.logger.debug("returning full timestamp col")
                v = test[timestamp_header]
                if in_minutes and v is not None:
                    v /= 60.0
        if in_minutes and v is not None:
            v /= 60.0
        return v


Instruction:
Can you create a Python 3 function that
returns discharge capacity in mAh / g and voltage.

Response:
def get_dcap(self, cycle=None, dataset_number=None):
        """Returns discharge_capacity (in mAh/g), and voltage."""

        #  TODO: should return a DataFrame as default
        #  but remark that we then have to update e.g. batch_helpers.py

        dataset_number = self._validate_dataset_number(dataset_number)
        if dataset_number is None:
            self._report_empty_dataset()
            return
        dc, v = self._get_cap(cycle, dataset_number, "discharge")
        return dc, v


Instruction:
Can you write a function in Python 3 where it
returns charge capacity in mAh / g and voltage.

Response:
def get_ccap(self, cycle=None, dataset_number=None):
        """Returns charge_capacity (in mAh/g), and voltage."""

        #  TODO: should return a DataFrame as default
        #  but remark that we then have to update e.g. batch_helpers.py

        dataset_number = self._validate_dataset_number(dataset_number)
        if dataset_number is None:
            self._report_empty_dataset()
            return
        cc, v = self._get_cap(cycle, dataset_number, "charge")
        return cc, v