Source code for openalea.fspmwheat.tools

# -*- coding: latin-1 -*-

import os

import numpy as np
import pandas as pd
from scipy import stats
import matplotlib.pyplot as plt

from openalea.adel.mtg import to_plantgl
from openalea.plantgl.all import Viewer, Vector3

"""
    fspmwheat.tools
    ~~~~~~~~~~~~~~~

    This module provides convenient tools needed by the facades.

"""


[docs] def combine_dataframes_inplace(model_dataframe, shared_column_indexes, shared_dataframe_to_update): """Combine `model_dataframe` and `shared_dataframe_to_update` in-place: * re-index `model_dataframe` and `shared_dataframe_to_update` by `shared_column_indexes`, * use method pd.DataFrame.combine_first(), * reset to the right types in `shared_dataframe_to_update`, * reorder the columns: first columns in `shared_column_indexes`, then others columns alphabetically, * and reset the index in `shared_dataframe_to_update`. :param pandas.DataFrame model_dataframe: dataframe to use for updating `shared_dataframe_to_update`. :param list shared_column_indexes: The indexes to re-index `model_dataframe` and `shared_dataframe_to_update` before combining them. :param pandas.DataFrame shared_dataframe_to_update: The dataframe to update. .. note:: `shared_dataframe_to_update` is updated in-place. Thus, `shared_dataframe_to_update` keeps the same object's memory address. """ # re-index the dataframes to have common indexes if len(shared_dataframe_to_update) == 0: shared_dataframe_to_update_reindexed = shared_dataframe_to_update else: shared_dataframe_to_update.sort_values(shared_column_indexes, inplace=True) shared_dataframe_to_update_reindexed = pd.DataFrame(shared_dataframe_to_update.values.tolist(), index=sorted(shared_dataframe_to_update.groupby(shared_column_indexes).groups.keys()), columns=shared_dataframe_to_update.columns) model_dataframe.sort_values(shared_column_indexes, inplace=True) model_dataframe_reindexed = pd.DataFrame(model_dataframe.values.tolist(), index=sorted(model_dataframe.groupby(shared_column_indexes).groups.keys()), columns=model_dataframe.columns) # combine model and shared re-indexed dataframes if model_dataframe_reindexed.empty and shared_dataframe_to_update.empty: new_shared_dataframe = model_dataframe_reindexed.copy() for new_header in shared_dataframe_to_update_reindexed.columns.difference(model_dataframe_reindexed.columns): new_shared_dataframe[new_header] = "" else: new_shared_dataframe = model_dataframe_reindexed.combine_first(shared_dataframe_to_update_reindexed) # reset to the right types in the combined dataframe dtypes = model_dataframe_reindexed.dtypes.combine_first(shared_dataframe_to_update_reindexed.dtypes) for column_name, data_type in dtypes.items(): if np.issubdtype(np.int64, data_type) and new_shared_dataframe[column_name].isnull().values.any(): # Used to keep bool values data_type = float # will return an error if data_type is integer new_shared_dataframe[column_name] = new_shared_dataframe[column_name].astype(data_type) # reorder the columns new_shared_dataframe = new_shared_dataframe.reindex(shared_column_indexes + sorted(new_shared_dataframe.columns.difference(shared_column_indexes)), axis=1) # update the shared dataframe in-place shared_dataframe_to_update.drop(shared_dataframe_to_update.index, axis=0, inplace=True) shared_dataframe_to_update.drop(shared_dataframe_to_update.columns, axis=1, inplace=True) shared_dataframe_to_update['dataframe_to_update_index'] = new_shared_dataframe.index shared_dataframe_to_update.set_index('dataframe_to_update_index', inplace=True) for column in new_shared_dataframe.columns: shared_dataframe_to_update[column] = new_shared_dataframe[column] shared_dataframe_to_update.reset_index(0, drop=True, inplace=True)
[docs] def plot_linear_regression(x_array, y_array, x_label='x', y_label='y', plot_filepath=None): """Perform a linear regression of `x_array` vs `y_array` and create a plot showing the fit against the original data. If `plot_filepath` is not None, save the plot to a PNG file. Otherwise display the plot. This is derived from http://learningzone.rspsoc.org.uk/index.php/Learning-Materials/Python-Scripting/6.4-Fitting-linear-equations, which is under license CC BY-NC-SA 3.0 (https://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_US). :param numpy.ndarray x_array: The first set of measurements. :param numpy.ndarray y_array: The second set of measurements. :param str x_label: The label of the abscissa axis. Default is 'x'. :param str y_label: The label of the ordinates axis. Default is 'y'. :param str plot_filepath: The file path to save the plot in. If `None`, do not save the plot. :Examples: >>> import pandas as pd >>> modelmaker_output_df = pd.read_csv('modelmaker_output.csv') # 'modelmaker_output.csv' must contain at least the column 'Sucrose_Phloem' >>> cnwheat_output_df = pd.read_csv('cnwheat_output.csv') # 'cnwheat_output.csv' must contain at least the column 'Sucrose_Phloem' >>> plot_linear_regression(modelmaker_output_df.Sucrose_Phloem, cnwheat_output_df.Sucrose_Phloem, x_label='modelmaker_{}'.format('Sucrose_Phloem'), y_label='cnwheat_{}'.format('Sucrose_Phloem'), plot_filepath='compare.png') """ # Perform fit (aCoeff, bCoeff, rVal, _, _) = stats.linregress(x_array, y_array) # Use fits to predict y output for a range of diameters x_samples_array = np.linspace(min(x_array), max(x_array), 1000) y_predict_array = aCoeff * x_samples_array + bCoeff # Create a string, showing the form of the equation (with fitted coefficients) and r squared value. # Coefficients are rounded to two decimal places. equation = 'y = {} x + {} (R$^2$ = {})'.format(round(aCoeff, 2), round(bCoeff, 2), round(rVal**2, 2)) plt.figure() # Plot fit against original data plt.plot(x_array, y_array, '.') plt.plot(x_samples_array, y_predict_array) plt.title('{} vs {}'.format(x_label, y_label)) x_label = 'x = {}'.format(x_label) plt.xlabel(x_label) y_label = 'y = {}'.format(y_label) plt.ylabel(y_label) plt.legend(['x vs y', equation]) # Save plot if plot_filepath is None: plt.show() else: plt.savefig(plot_filepath, dpi=200, format='PNG') plt.close()
[docs] def color_MTG_Nitrogen(g, df, t, SCREENSHOT_DIRPATH): def color_map(N): if 0 <= N <= 0.5: # TODO: organe senescent (prendre prop) vid_colors = [150, 100, 0] elif 0.5 < N < 5: # Fvertes vid_colors = [int(255 - N*51), int(255 - N * 20), 50] else: vid_colors = [0, 155, 0] return vid_colors def calculate_Total_Organic_Nitrogen(amino_acids, proteins, Nstruct): """Total amount of organic N (amino acids + proteins + Nstruct). :param float amino_acids: Amount of amino acids (µmol N) :param float proteins: Amount of proteins (µmol N) :param float Nstruct: Structural N mass (g) :return: Total amount of organic N (mg) :rtype: float """ return (amino_acids + proteins) * 14E-3 + Nstruct * 1E3 colors = {} groups_df = df.groupby(['plant', 'axis', 'metamer', 'organ', 'element']) for vid in g.components_at_scale(g.root, scale=5): pid = int(g.index(g.complex_at_scale(vid, scale=1))) axid = g.property('label')[g.complex_at_scale(vid, scale=2)] mid = int(g.index(g.complex_at_scale(vid, scale=3))) org = g.property('label')[g.complex_at_scale(vid, scale=4)] elid = g.property('label')[vid] id_map = (pid, axid, mid, org, elid) if id_map in groups_df.groups.keys(): N = (g.property('proteins')[vid] * 14E-3) / groups_df.get_group(id_map)['mstruct'].iloc[0] # N = (calculate_Total_Organic_Nitrogen(g.property('amino_acids')[vid], g.property('proteins')[vid], g.property('Nstruct')[vid])) / g.property('mstruct')[vid] colors[vid] = color_map(N) else: g.property('geometry')[vid] = None # plantgl s = to_plantgl(g, colors=colors)[0] Viewer.add(s) Viewer.camera.setPosition(Vector3(83.883, 12.3239, 93.4706)) Viewer.camera.lookAt(Vector3(0., 0, 50)) Viewer.saveSnapshot(os.path.join(SCREENSHOT_DIRPATH, 'Day_{}.png'.format(t/24+1)))