sankey — driver → action
dalam merancang sebuah aksi, hal yang diperlukan adalah menghubungkan antara permasalahan/tantangan (driver) yang dihadapi, serta menemukan penyebab utama (underlying causes) dengan aksi (action) yang dilakukan.
dalam pelaksanaan program karbon hutan, identifikasi driver dapat dilakukan dengan menggunakan dua layer: (1) perubahan tutupan hutan dan lahan; dan (2) wilayah penguasaan, pemilikan, perijinan dan persetujuan berusaha. dari dua layer ini, dapat diidentifikasi apa yang menyebabkan (driver) dari deforestasi dan degradasi hutan. mengapa layer kedua menjadi penting? karena bisa jadi tutupan hutan dan lahan berupa belukar, belukar rawa, maupun lahan terbuka, sehingga pertanyaannya adalah apa yang menyebabkannya?
Get Ade Fadli’s stories in your inbox
Join Medium for free to get updates from this writer.
sankey diagram akan membantu untuk membaca perubahan. tidak hanya pada satu titik perubahan, namun bisa dilihat rentetat perubahan. pada bagian ujung perubahan, dapat diidentifikasi penyebab utama, sehingga dari titik sisi akhir tersebut, juga bisa diidentifikasi aksi apa yang diperlukan untuk dilakukan. sehingga dapat dengan mudah dijelaskan, mengapa aksi tersebut diperlukan.
import pandas as pd
import plotly.graph_objects as go
import plotly.colors as pc
import numpy as np
def hex_to_rgba(hex_color, opacity=0.2):
"""Converts a hex color to an RGBA string with given opacity."""
hex_color = hex_color.lstrip('#')
if len(hex_color) == 6:
r, g, b = int(hex_color[0:2], 16), int(hex_color[2:4], 16), int(hex_color[4:6], 16)
return f'rgba({r}, {g}, {b}, {opacity})'
return hex_color
def darken_hex(hex_color, factor=1.0):
"""Darkens a hex color by a given factor (0.0 to 1.0)."""
if not hex_color.startswith('#'):
return hex_color
hex_color = hex_color.lstrip('#')
try:
r = int(hex_color[0:2], 16)
g = int(hex_color[2:4], 16)
b = int(hex_color[4:6], 16)
r = int(r * factor)
g = int(g * factor)
b = int(b * factor)
return f'#{r:02x}{g:02x}{b:02x}'
except (ValueError, IndexError):
return hex_color
def main():
file_path = 'tuplah.xlsx'
output_file = 'driver.html'
print(f"Loading {file_path}...")
df = pd.read_excel(file_path)
# Identify time step columns and value column
time_cols = [c for c in df.columns if c.startswith('Landcover')]
value_col = 'Luas (ha)'
if not time_cols:
print("Error: No time step columns found.")
return
print(f"Processing {len(df)} rows across {len(time_cols)} time steps...")
# 1. Identify all unique landcover types across all time steps for consistent coloring
all_landcovers = set()
for col in time_cols:
all_landcovers.update(df[col].dropna().unique())
all_landcovers = sorted(list(all_landcovers))
print(f"Found {len(all_landcovers)} unique landcover types: {all_landcovers}")
# 2. Map each landcover type to a unique color
colorscale = pc.sequential.Viridis
landcover_colors = {
lc: pc.sample_colorscale(colorscale, i / len(all_landcovers) + 0.05)[0]
for i, lc in enumerate(all_landcovers)
}
nodes_labels = [] # The visual labels (Landcover Name)
node_ids = [] # Unique identifiers (Year, Landcover) to distinguish stages
node_map = {} # node_id -> index mapping
def get_node_idx(time_step, lc_value):
# Use year + landcover as a unique key for the node's position in the graph
node_id = (time_step, str(lc_value))
if node_id not in node_map:
node_map[node_id] = len(nodes_labels)
nodes_labels.append(str(lc_value)) # Show ONLY landcover name in label
node_ids.append(node_id)
return node_map[node_id]
sources = []
targets = []
values = []
# 3. Build flows
for i in range(len(time_cols) - 1):
curr_col = time_cols[i]
next_col = time_cols[i+1]
temp_df = df[[curr_col, next_col, value_col]].copy()
temp_df[curr_col] = temp_df[curr_col].astype(str)
temp_df[next_col] = temp_df[next_col].astype(str)
temp_df = temp_df[temp_df[value_col] > 0]
if temp_df.empty:
continue
flows = temp_df.groupby([curr_col, next_col, value_col]).sum().reset_index()
for _, row in flows.iterrows():
src_idx = get_node_idx(curr_col, row[curr_col])
tgt_idx = get_node_idx(next_col, row[next_col])
sources.append(src_idx)
targets.append(tgt_idx)
values.append(row[value_col])
if not nodes_labels:
print("No valid flows found.")
return
# 4. Prepare node colors (consistent for same landcover, making them darker)
node_colors = []
for node_id in node_ids:
lc_val = node_id[1]
matched_color = "gray"
for original_lc, color in landcover_colors.items():
if str(original_lc) == lc_val:
matched_color = color
break
node_colors.append(darken_hex(matched_color, factor=0.9))
# 5. Prepare link colors (same as original bright color of target node with transparency)
link_colors = []
for t_idx in targets:
target_node_id = node_ids[t_idx]
lc_val = target_node_id[1]
matched_color = "gray"
for original_lc, color in landcover_colors.items():
if str(original_lc) == lc_val:
matched_color = color
break
link_colors.append(hex_to_rgba(matched_color, opacity=0.2))
print(f"Generating Sankey diagram with {len(nodes_labels)} nodes...")
fig = go.Figure(data=[go.Sankey(
node=dict(
pad=25,
thickness=30,
line=dict(color="gray", width=0.5),
label=nodes_labels,
color=node_colors
),
link=dict(
source=sources,
target=targets,
value=values,
color=link_colors
)
)])
# Add year info in the title to indicate the time sequence
years_sequence = [c.replace('Landcover ', '') for c in time_cols]
year_text = " -> ".join(years_sequence)
fig.update_layout(
title_text=f"Forest & Land cover change ({year_text})",
font_size=12,
width=1200,
height=800
)
fig.write_html(output_file)
print(f"Success! Diagram saved to: {output_file}")
if __name__ == "__main__":
main()sankey diagram sangat membantu untuk membaca perubahan. dari grafik misalnya dapat terlihat jelas, dari tiga titik waktu, ada yang kembali berubah dalam rentetan waktu. ada yang tumbuh kembali, dan ada yang tumbuh lalu menghilang.
kesalahan yang kerap muncul dalam penyusunan aksi adalah kegagalan dalam menjelaskan mengapa aksi diperlukan untuk dilakukan. dengan ada layer [penguasaan, pemilikan, perijinan, atau persetujuan pengusahaan], maka aksi akan melekat pada “pemilik” wilayah sebagai pelaku utama. dan ini juga diuntungkan dengan dapat menunjuk dengan “baik” lokasi aksi akan dilakukan. dengan penjelasan sederhana, maka validator dan verifikator dapat memahami mengapa aksi dapat mengatasi driver deforestasi dan degradasi hutan. ini bagian yang “kerap” dilewatkan oleh penyusun aksi.
#gitusih









