Source code for openalea.cnwheat.converter

# -*- coding: latin-1 -*-
import numpy as np
import pandas as pd

from openalea.cnwheat import model, simulation

"""
    cnwheat.converter
    ~~~~~~~~~~~~~~~~~

    The module :mod:`cnwheat.converter` defines functions to convert
    :class:`dataframes <pandas.DataFrame>` to/from CN-Wheat inputs or outputs format.

"""

#: the columns of the outputs dataframe at PLANT scale
PLANTS_VARIABLES = simulation.Simulation.PLANTS_INDEXES + simulation.Simulation.PLANTS_RUN_VARIABLES

#: the columns of the outputs dataframe at AXIS scale
AXES_VARIABLES = simulation.Simulation.AXES_INDEXES + simulation.Simulation.AXES_RUN_VARIABLES

#: the columns of the outputs dataframe at PHYTOMER scale
PHYTOMERS_VARIABLES = simulation.Simulation.PHYTOMERS_INDEXES + simulation.Simulation.PHYTOMERS_RUN_VARIABLES

#: the columns of the outputs dataframe at ORGAN scale
ORGANS_VARIABLES = simulation.Simulation.ORGANS_INDEXES + simulation.Simulation.ORGANS_RUN_VARIABLES

#: the columns of the outputs dataframe at HIDDEN ZONE scale
HIDDENZONE_VARIABLES = simulation.Simulation.HIDDENZONE_INDEXES + simulation.Simulation.HIDDENZONE_RUN_VARIABLES
HIDDENZONE_OUTPUTS_VARIABLES = HIDDENZONE_VARIABLES
HIDDENZONE_OUTPUTS_RUN_VARIABLES = simulation.Simulation.HIDDENZONE_RUN_VARIABLES

#: the columns of the outputs dataframe at ELEMENT scale
ELEMENTS_VARIABLES = simulation.Simulation.ELEMENTS_INDEXES + simulation.Simulation.ELEMENTS_RUN_VARIABLES
ELEMENTS_OUTPUTS_VARIABLES = ELEMENTS_VARIABLES
ELEMENTS_OUTPUTS_RUN_VARIABLES = simulation.Simulation.ELEMENTS_RUN_VARIABLES

#: the columns of the outputs dataframe at SOIL scale
SOILS_VARIABLES = simulation.Simulation.SOILS_INDEXES + simulation.Simulation.SOILS_RUN_VARIABLES

#: the mapping of the CN-Wheat organ classes to organ names in MTG
CNWHEAT_CLASSES_TO_DATAFRAME_ORGANS_MAPPING = {model.Internode: 'internode', model.Lamina: 'blade',
                                               model.Sheath: 'sheath', model.Peduncle: 'peduncle', model.Chaff: 'ear',
                                               model.Roots: 'roots', model.Grains: 'grains', model.Phloem: 'phloem', model.HiddenZone: 'hiddenzone'}

#: the mapping of the name of each element, from Dataframe to CN-Wheat
DATAFRAME_TO_CNWHEAT_ELEMENTS_NAMES_MAPPING = {'HiddenElement': 'enclosed_element', 'StemElement': 'exposed_element', 'LeafElement1': 'exposed_element'}


[docs] def from_dataframes(organs_inputs=None, hiddenzones_inputs=None, elements_inputs=None, soils_inputs=None, update_parameters=None): """ If `organs_inputs`, `hiddenzones_inputs` and `elements_inputs` are not `None`, convert `organs_inputs`, `hiddenzones_inputs` and `elements_inputs` to a :class:`population <model.Population>`. If `soils_inputs` is not `None`, convert `soils_inputs` to a dictionary of :class:`soils <model.Soil>`. :param pandas.DataFrame organs_inputs: Organs inputs, with one line by organ. :param pandas.DataFrame hiddenzones_inputs: Hidden zones inputs, with one line by hidden zone. :param pandas.DataFrame elements_inputs: Elements inputs, with one line by element. :param pandas.DataFrame soils_inputs: Soils inputs, with one line by soil. :param dict update_parameters: A dictionary with the parameters to update, should have the form {'Organ_label1': {'param1': value1, 'param2': value2}, ...}. :return: If `organs_inputs`, `hiddenzones_inputs` and `elements_inputs` are not `None`, return a :class:`population <model.Population>`, and/or if `soils_inputs` is not `None`, return a :class:`dict` of :class:`soils <model.Soil>`. :rtype: (pandas.DataFrame, dict) or dict """ if update_parameters is None: update_parameters = {} convert_dataframes_to_population = organs_inputs is not None and hiddenzones_inputs is not None and elements_inputs is not None convert_dataframe_to_soils_dict = soils_inputs is not None if convert_dataframes_to_population: population = model.Population() for plant_index in organs_inputs.plant.unique(): # create a new plant plant = model.Plant(plant_index) population.plants.append(plant) curr_axes_labels = organs_inputs[organs_inputs['plant'] == plant_index].axis.unique() for axis_label in curr_axes_labels: # create a new axis axis = model.Axis(axis_label) curr_organs_inputs = organs_inputs[(organs_inputs['plant'] == plant_index) & (organs_inputs['axis'] == axis_label)] for axis_attribute_name, axis_attribute_class in (('roots', model.Roots), ('phloem', model.Phloem), ('grains', model.Grains)): organ_label = CNWHEAT_CLASSES_TO_DATAFRAME_ORGANS_MAPPING[axis_attribute_class] organ_inputs = curr_organs_inputs[curr_organs_inputs['organ'] == organ_label] if not organ_inputs.empty: # create a new organ organ = axis_attribute_class(organ_label) organ_attributes_names = [state_var_name for state_var_name in simulation.Simulation.ORGANS_STATE if hasattr(organ, state_var_name)] organ_row = organ_inputs.loc[organ_inputs.first_valid_index()] organ_attributes_values = organ_row[organ_attributes_names].tolist() organ_attributes = dict(zip(organ_attributes_names, organ_attributes_values)) organ.__dict__.update(organ_attributes) # Update parameters if specified if organ_label in update_parameters: organ.PARAMETERS.__dict__.update(update_parameters[organ_label]) organ.initialize() setattr(axis, axis_attribute_name, organ) curr_metamers_indexes_for_hiddenzones = hiddenzones_inputs[(hiddenzones_inputs['plant'] == plant_index) & (hiddenzones_inputs['axis'] == axis_label)].metamer.unique() curr_metamers_indexes_for_elements = elements_inputs[(elements_inputs['plant'] == plant_index) & (elements_inputs['axis'] == axis_label)].metamer.unique() curr_metamers_indexes = np.unique(np.concatenate((curr_metamers_indexes_for_hiddenzones, curr_metamers_indexes_for_elements))) for metamer_index in curr_metamers_indexes: # create a new phytomer phytomer = model.Phytomer(metamer_index) axis.phytomers.append(phytomer) for phytomer_attribute_name, phytomer_attribute_class, phytomer_attribute_element_class in \ (('chaff', model.Chaff, model.ChaffElement), ('lamina', model.Lamina, model.LaminaElement), ('internode', model.Internode, model.InternodeElement), ('peduncle', model.Peduncle, model.PeduncleElement), ('sheath', model.Sheath, model.SheathElement)): organ_label = CNWHEAT_CLASSES_TO_DATAFRAME_ORGANS_MAPPING[phytomer_attribute_class] if metamer_index in curr_metamers_indexes_for_elements: curr_elements_inputs = elements_inputs[(elements_inputs['plant'] == plant_index) & (elements_inputs['axis'] == axis_label) & (elements_inputs['metamer'] == metamer_index) & (elements_inputs['organ'] == organ_label)] if organ_label not in curr_elements_inputs.organ.values: continue # create a new organ organ = phytomer_attribute_class(organ_label) # Update parameters if specified if 'PhotosyntheticOrgan' in update_parameters: organ.PARAMETERS.__dict__.update(update_parameters['PhotosyntheticOrgan']) organ.initialize() setattr(phytomer, phytomer_attribute_name, organ) for mtg_element_label, cnwheat_element_name in DATAFRAME_TO_CNWHEAT_ELEMENTS_NAMES_MAPPING.items(): element_inputs = curr_elements_inputs[curr_elements_inputs['element'] == mtg_element_label] if len(element_inputs) == 0: continue element_inputs = element_inputs.loc[:, simulation.Simulation.ELEMENTS_STATE] element_dict = element_inputs.loc[element_inputs.first_valid_index()].to_dict() # create a new element element = phytomer_attribute_element_class(mtg_element_label, **element_dict) # Add parameters from organ scale element.PARAMETERS.__dict__.update(organ.PARAMETERS.__dict__) setattr(organ, cnwheat_element_name, element) if metamer_index in curr_metamers_indexes_for_hiddenzones: hiddenzone_inputs = hiddenzones_inputs[(hiddenzones_inputs['plant'] == plant_index) & (hiddenzones_inputs['axis'] == axis_label) & (hiddenzones_inputs['metamer'] == metamer_index)] if len(hiddenzone_inputs) == 0: continue hiddenzone_inputs = hiddenzone_inputs.loc[:, simulation.Simulation.HIDDENZONE_STATE] hiddenzone_dict = hiddenzone_inputs.loc[hiddenzone_inputs.first_valid_index()].to_dict() # create a new hidden zone hiddenzone = model.HiddenZone(CNWHEAT_CLASSES_TO_DATAFRAME_ORGANS_MAPPING[model.HiddenZone], **hiddenzone_dict) # Update parameters if specified if hiddenzone.label in update_parameters: hiddenzone.PARAMETERS.__dict__.update(update_parameters[hiddenzone.label]) hiddenzone.initialize() phytomer.hiddenzone = hiddenzone plant.axes.append(axis) if convert_dataframe_to_soils_dict: soils = {} for soil_id, soil_group in soils_inputs.groupby(simulation.Simulation.SOILS_INDEXES): # create a new soil soil_attributes = soil_group.loc[soil_group.first_valid_index(), simulation.Simulation.SOILS_STATE].to_dict() soil = model.Soil(**soil_attributes) soils[soil_id] = soil if convert_dataframes_to_population and convert_dataframe_to_soils_dict: return population, soils elif convert_dataframes_to_population: return population else: return soils
[docs] def to_dataframes(population=None, soils=None): """ Convert a CN-Wheat :class:`population <model.Population>` and/or a dictionary of :class:`soils <model.Soil>` to Pandas dataframes. If `population` is not None, convert `population` to Pandas dataframes. If `soils` is not None, convert `soils` to Pandas dataframe. :param model.Population population: The CN-Wheat population to convert. :param dict soils: The soils to convert. :return: If `population` is not None, return :class:`dataframes <pandas.DataFrame>` describing the internal state and compartments of the population at each scale: * plant scale: plant index, state parameters, state variables, intermediate variables, fluxes and integrative variables of each plant (see :mod:`PLANTS_VARIABLES`) * axis scale: plant index, axis id, state parameters, state variables, intermediate variables, fluxes and integrative variables of each axis (see :mod:`AXES_VARIABLES`) * phytomer scale: plant index, axis id, phytomer index, state parameters, state variables, intermediate variables, fluxes and integrative variables of each phytomer (see :mod:`PHYTOMERS_VARIABLES`) * organ scale: * hidden zones: plant index, axis id, phytomer index, state parameters, state variables, intermediate variables, fluxes and integrative variables of each hidden zone (see :mod:`HIDDENZONE_VARIABLES`) * roots, phloem and grains: plant index, axis id, organ type, state parameters, state variables, intermediate variables, fluxes and integrative variables of each organ (see :mod:`ORGANS_VARIABLES`) * and element scale: plant index, axis id, phytomer index, organ type, element type, state parameters, state variables, intermediate variables, fluxes and integrative variables of each element (see :mod:`ELEMENTS_VARIABLES`) and/or if `soils` is not None, return a :class:`dataframe <pandas.DataFrame>` describing internal state and compartments of the soils, with one line per soil: * plant index, axis id, state parameters, state variables, intermediate variables, fluxes and integrative variables of each soil (see :mod:`SOILS_RUN_VARIABLES`) :rtype: (pandas.DataFrame) """ convert_population_to_dataframes = population is not None convert_soils_to_dataframe = soils is not None def append_row(model_object, indexes, attributes_names, inputs_df): # function to append a row to a dataframe attributes_values = [] for attribute_name in attributes_names: attributes_values.append(getattr(model_object, attribute_name, np.nan)) inputs_df.loc[len(inputs_df), :] = indexes + attributes_values if convert_population_to_dataframes: # initialize the dataframes all_plants_df = pd.DataFrame(columns=PLANTS_VARIABLES) all_axes_df = pd.DataFrame(columns=AXES_VARIABLES) all_phytomers_df = pd.DataFrame(columns=PHYTOMERS_VARIABLES) all_organs_df = pd.DataFrame(columns=ORGANS_VARIABLES) all_hiddenzones_df = pd.DataFrame(columns=HIDDENZONE_OUTPUTS_VARIABLES) all_elements_df = pd.DataFrame(columns=ELEMENTS_OUTPUTS_VARIABLES) # run through the population tree and fill the dataframes for plant in population.plants: append_row(plant, [plant.index], simulation.Simulation.PLANTS_RUN_VARIABLES, all_plants_df) for axis in plant.axes: append_row(axis, [plant.index, axis.label], simulation.Simulation.AXES_RUN_VARIABLES, all_axes_df) for organ in (axis.roots, axis.phloem, axis.grains): if organ is not None: append_row(organ, [plant.index, axis.label, organ.label], simulation.Simulation.ORGANS_RUN_VARIABLES, all_organs_df) for phytomer in axis.phytomers: append_row(phytomer, [plant.index, axis.label, phytomer.index], simulation.Simulation.PHYTOMERS_RUN_VARIABLES, all_phytomers_df) if phytomer.hiddenzone is not None: append_row(phytomer.hiddenzone, [plant.index, axis.label, phytomer.index], HIDDENZONE_OUTPUTS_RUN_VARIABLES, all_hiddenzones_df) for organ in (phytomer.chaff, phytomer.peduncle, phytomer.lamina, phytomer.internode, phytomer.sheath): if organ is None: continue for element in (organ.exposed_element, organ.enclosed_element): if element is None: continue append_row(element, [plant.index, axis.label, phytomer.index, organ.label, element.label], ELEMENTS_OUTPUTS_RUN_VARIABLES, all_elements_df) # sort the rows of the dataframes by columns all_plants_df.sort_values(by=PLANTS_VARIABLES, inplace=True) all_axes_df.sort_values(by=AXES_VARIABLES, inplace=True) all_phytomers_df.sort_values(by=PHYTOMERS_VARIABLES, inplace=True) all_organs_df.sort_values(by=ORGANS_VARIABLES, inplace=True) all_hiddenzones_df.sort_values(by=HIDDENZONE_OUTPUTS_VARIABLES, inplace=True) all_elements_df.sort_values(by=ELEMENTS_OUTPUTS_VARIABLES, inplace=True) # convert the indexes of plants, metamers and elements to integers in the dataframes all_plants_df['plant'] = all_plants_df['plant'].astype(int) all_axes_df['plant'] = all_axes_df['plant'].astype(int) all_phytomers_df[['plant', 'metamer']] = all_phytomers_df[['plant', 'metamer']].astype(int) all_organs_df['plant'] = all_organs_df['plant'].astype(int) all_hiddenzones_df[['plant', 'metamer']] = all_hiddenzones_df[['plant', 'metamer']].astype(int) all_elements_df[['plant', 'metamer']] = all_elements_df[['plant', 'metamer']].astype(int) all_plants_df.reset_index(drop=True, inplace=True) all_axes_df.reset_index(drop=True, inplace=True) all_phytomers_df.reset_index(drop=True, inplace=True) all_organs_df.reset_index(drop=True, inplace=True) all_hiddenzones_df.reset_index(drop=True, inplace=True) all_elements_df.reset_index(drop=True, inplace=True) if convert_soils_to_dataframe: all_soils_df = pd.DataFrame(columns=SOILS_VARIABLES) for soil_id, soil in soils.items(): append_row(soil, list(soil_id), simulation.Simulation.SOILS_RUN_VARIABLES, all_soils_df) all_soils_df.sort_values(by=SOILS_VARIABLES, inplace=True) all_soils_df['plant'] = all_soils_df['plant'].astype(int) all_soils_df.reset_index(drop=True, inplace=True) if convert_population_to_dataframes and convert_soils_to_dataframe: return all_plants_df, all_axes_df, all_phytomers_df, all_organs_df, all_hiddenzones_df, all_elements_df, all_soils_df elif convert_population_to_dataframes: return all_plants_df, all_axes_df, all_phytomers_df, all_organs_df, all_hiddenzones_df, all_elements_df else: return all_soils_df