#!/usr/bin/python3 # -*- coding: utf-8 -*- import pandas as pd import numpy as np import os from patutils import md5sum from Indicator import Indicator from overrides import overrides class Social(Indicator): ''' This function is used to calculate the social indice. The idea is to propose a transition matrix for each type of culture and use it to visualize the conversion cost of the total conversion between the initial scenario and the old one. Each transition has a value between 0 and 1. These values were defined during reunions and with the review of our experts. ''' @overrides def __init__(self, social_config, initial_patches, patches_md5sum, targetPAT=None): ''' :param initial_patches Initial state of the patches (attributes cultugeopat and SURF_PARC required) :param patches_md5sum md5 checksum of the input file of patches :param social_config dict giving parameters :param targetPAT is ignored in this implementation ''' self.patches_md5sum = patches_md5sum self.matrice_transition = pd.read_csv(social_config['cost_matrix_filename'], sep='\t',index_col=0) self.cost_matrix_md5sum = md5sum(social_config['cost_matrix_filename']) # proportion of a cultural type needed for considering that this cultural type # is suffisantly present in the locality for ignoring the conversion cost. self.bdv_threshold = social_config['bdv_threshold'] costs_filename = social_config['patches_costs_filename'] if not os.path.isfile(costs_filename): self.compute_patches_transition_cost(initial_patches) self.save_patches_transition_cost(costs_filename) else: self.load_patches_transition_cost(costs_filename) def compute_patches_transition_cost(self, initial_patches): costs=[] for bdv,grouped in initial_patches.groupby('Bdv'): grouped=grouped.copy() surfaces_initial = grouped.groupby('cultgeopat')['SURF_PARC'].sum() cult_threshold = surfaces_initial > surfaces_initial.sum() * self.bdv_threshold for culture in self.matrice_transition.columns.get_values(): for row in initial_patches.itertuples(): cost = 0.0 # patches that have different farming that others in same Bdv if culture not in cult_threshold or not cult_threshold[culture]: # patches that have different farming that others of the same farmer if culture not in initial_patches[initial_patches['id_expl']==row.id_expl]['cultgeopat']: cost = self.matrice_transition.loc[row.cultgeopat,culture] costs.append([row.ID_PARCEL, row.cultgeopat, culture, cost]) self.patches_transition_cost = pd.DataFrame(costs, columns=['ID_PARCEL','initialcult','cultgeopat','cost']) def save_patches_transition_cost(self, filename): ''' Save the matrix of costs computed for each patch and initial parameters in a file (npz format). ''' m = self.patches_transition_cost.values np.savez(filename, bdv_threshold=self.bdv_threshold, patches_md5sum=self.patches_md5sum, cost_matrix_md5sum=self.cost_matrix_md5sum, data=m.data) def load_patches_transition_cost(self, filename): ''' Load the matrix of costs and initial parameters from a file (npz format). The correspondance will be checked between class attributes and metadata stored in the file ''' loader = np.load(filename) error_message = ('Matrix metadata {0} does\'nt correspond to metadata given at indicator construction' '\n\t{0} in metadata: {1} while asked: {2}' '\n\tmatrix filename: ') + filename for k1,k2 in [[self.bdv_threshold,'bdv_threshold'],[self.patches_md5sum,'patches_md5sum'],[self.cost_matrix_md5sum,'cost_matrix_md5sum']]: if k2 not in loader or k1!=loader[k2]: raise ValueError(error_message.format(k2, dict(loader.items()).get(k2, 'NA'), k1)) self.patches_transition_cost = pd.DataFrame(loader['data'], columns=['ID_PARCEL','initialcult','cultgeopat','cost']) @overrides def compute_indicator(self, scenario_patches): ''' :param scenario: The scenario to analyse ''' cost = 0 patches_altered = scenario_patches[scenario_patches['cultgeopat'] != scenario_patches['init_cult']] cost = pd.merge(patches_altered, self.patches_transition_cost,on=['ID_PARCEL','cultgeopat'], how='left')['cost'].sum() return 1/(1+cost) if __name__ == '__main__': import time from patutils import load_pat_patches # loading and initializing patches data patches_filename = "../output/PAT_patches/PAT_patches.shp" matrix_filename = '../resources/matrice_transition.csv' patches = load_pat_patches(patches_filename) # init indicator social = Social({ 'cost_matrix_filename': matrix_filename, 'bdv_threshold': 0.1, 'patches_costs_filename': '../output/patches_transition_costs.npz' }, patches, md5sum(patches_filename)) # computing init score scenario_patches = patches.copy() print(social.compute_indicator(scenario_patches)) # small reallocation and computing score scenario_patches.loc[1,'cultgeopat']='Fruits et légumes' # should give 1/(1+1) the cost for one transition to Fruits et légumes print(social.compute_indicator(scenario_patches)) # computing with more reallocation for N in [200,500,2000]:#,10000]: for i in range(N): scenario_patches.ix[i,'cultgeopat']='Protéagineux' print('{} {}'.format(N, social.compute_indicator(scenario_patches)))