social.py 5.74 KB
Newer Older
1
2
#!/usr/bin/python3
# -*- coding: utf-8 -*-
3
import pandas as pd
4
5
import numpy as np
import os
6
import patutils
7
8
9
from patutils import md5sum
from Indicator import Indicator
from overrides import overrides
10

11
class Social(Indicator):
12
13
	'''
	This function is used to calculate the social indice.
14
15
	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.
16

17
18
19
	Each transition has a value between 0 and 1.
	These values were defined during reunions and with the review of our experts.
	'''
20

21
22
	@overrides
	def __init__(self, social_config, initial_patches, patches_md5sum, targetPAT=None):
23
		'''
24
25
		: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
26
27
		:param social_config dict giving parameters
		:param targetPAT is ignored in this implementation
28
		'''
29
		self.patches_md5sum = patches_md5sum
30
		self.matrice_transition = pd.read_csv(social_config['cost_matrix_filename'], sep='\t',index_col=0)
31
32
		self.matrice_transition.rename(index=patutils.code_cultgeopat, inplace=True)
		self.matrice_transition.rename(columns=patutils.code_cultgeopat, inplace=True)
33
34
35
36
37
38
39
40
41
42
		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)
43
44
			try:
				self.patches_transition_cost.replace({'initialcult':patutils.code_cultgeopat,'cultgeopat':patutils.code_cultgeopat},inplace=True)
Dumoulin Nicolas's avatar
Dumoulin Nicolas committed
45
			except Exception:
46
47
48
				pass
		self.patches_transition_cost.set_index('ID_PARCEL', inplace=True)
		self.patches_transition_cost.index = self.patches_transition_cost.index.astype('int')
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

	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]
64
				costs.append([row.Index, row.cultgeopat, culture, cost])
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
		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'])
90

91
	@overrides
92
	def compute_indicator(self, scenario_patches):
93
94
95
96
		'''
		:param scenario: The scenario to analyse
		'''
		cost = 0
97
98
		patches_altered = scenario_patches[scenario_patches['cultgeopat'] != scenario_patches['init_cult']]['cultgeopat']
		cost += pd.merge(patches_altered, self.patches_transition_cost, on=['ID_PARCEL','cultgeopat'], how='left')['cost'].sum()
99
		return cost
100
101

if __name__ == '__main__':
102
	import time
103
	from patutils import load_pat_patches, code_cultgeopat
104
105
106
	# loading and initializing patches data
	patches_filename = "../output/PAT_patches/PAT_patches.shp"
	matrix_filename = '../resources/matrice_transition.csv'
107
	patches = load_pat_patches(patches_filename)
108
	# init indicator
109
110
111
112
	social = Social({
			'cost_matrix_filename': matrix_filename, 'bdv_threshold': 0.1,
			'patches_costs_filename': '../output/patches_transition_costs.npz'
		}, patches, md5sum(patches_filename))
113
	# computing init score
114
	scenario_patches = patches.copy()
115
	print(social.compute_indicator(scenario_patches))
116
	# small reallocation and computing score
117
118
	scenario_patches.loc[182321,'cultgeopat']=code_cultgeopat['Oléagineux']
	# should give 0.5 the cost for one transition to Oléagineux
119
	print(social.compute_indicator(scenario_patches))
120
	# computing with more reallocation
121
	cult_col_index = scenario_patches.columns.get_loc('cultgeopat')
122
	for N in [200,500,2000]:#,10000]:
123
		for i in range(N):
124
			scenario_patches.iat[i,cult_col_index]=code_cultgeopat['Fruits et légumes']
125
		print('{} {}'.format(N, social.compute_indicator(scenario_patches)))