— Directories

# Set working directory
setwd("D:/Mes Donnees/OAF_CIRAD/Farms_Selection_27A")

# Define output folder
output_dir <- "D:/Mes Donnees/OAF_CIRAD/Farms_Selection_27A/outputs"

# Create the output folder if it doesn't already exist
if (!dir.exists(output_dir)) {
  dir.create(output_dir, recursive = TRUE)
}

# Quick check
getwd()
## [1] "D:/Mes Donnees/OAF_CIRAD/Farms_Selection_27A"
output_dir
## [1] "D:/Mes Donnees/OAF_CIRAD/Farms_Selection_27A/outputs"

— Packages

— Databases

# Load required package for Excel files
library(readxl)

# Define the raw data folder path
data_raw <- file.path(getwd(), "data_raw")

# --- File 1: X26B_CAs_farmers.xlsx ---
selection_CA1 <- read_excel(
  file.path(data_raw, "26B CAs farmers.xlsx"),
  sheet = "26B CA2_Mz | Bean rotation"
)
## New names:
## • `Region` -> `Region...2`
## • `` -> `...14`
## • `` -> `...15`
## • `` -> `...16`
## • `Category` -> `Category...17`
## • `Nbr of farmers` -> `Nbr of farmers...18`
## • `` -> `...19`
## • `Region` -> `Region...20`
## • `Category` -> `Category...21`
## • `Nbr of farmers` -> `Nbr of farmers...22`
selection_CA2 <- read_excel(
  file.path(data_raw, "26B CAs farmers.xlsx"),
  sheet = "26B CA3_MzBean IC"
)
## New names:
## • `` -> `...14`
## • `` -> `...17`
# --- File 2: CONV_CA_Ratio.csv ---
yield_CA1 <- read.csv(
  file.path(data_raw, "CONV_CA_Ratio.csv"),
  stringsAsFactors = FALSE
)

# --- File 3: CA3 data.xlsx (3 sheets) ---
yield_CA2_25A <- read_excel(
  file.path(data_raw, "CA3 data.xlsx"),
  sheet = "25A CA3"
)
## New names:
## • `seed grams planted` -> `seed grams planted...29`
## • `seed grams planted` -> `seed grams planted...31`
## • `binnage2done` -> `binnage2done...149`
## • `binnage2done` -> `binnage2done...150`
## • `rustpresent.x` -> `rustpresent.x...235`
## • `rustpresent.x` -> `rustpresent.x...245`
## • `numberplantssampletaken` -> `numberplantssampletaken...247`
## • `numberplantssampletaken` -> `numberplantssampletaken...248`
## • `weedsreduceharvest` -> `weedsreduceharvest...293`
## • `weedfirstspecies` -> `weedfirstspecies...294`
## • `othermainspecies` -> `othermainspecies...295`
## • `firstspeciespicture` -> `firstspeciespicture...296`
## • `firstspeciespictureURL` -> `firstspeciespictureURL...297`
## • `weedsecondspecies` -> `weedsecondspecies...298`
## • `secondspeciesother` -> `secondspeciesother...299`
## • `secondspeciespicture` -> `secondspeciespicture...300`
## • `secondspeciespictureURL` -> `secondspeciespictureURL...301`
## • `greencoverpercentageharvest` -> `greencoverpercentageharvest...302`
## • `greencoverpicture` -> `greencoverpicture...303`
## • `greencoverpictureURL` -> `greencoverpictureURL...304`
## • `weedsreduceharvest` -> `weedsreduceharvest...306`
## • `weedfirstspecies` -> `weedfirstspecies...307`
## • `othermainspecies` -> `othermainspecies...308`
## • `firstspeciespicture` -> `firstspeciespicture...309`
## • `firstspeciespictureURL` -> `firstspeciespictureURL...310`
## • `weedsecondspecies` -> `weedsecondspecies...311`
## • `secondspeciesother` -> `secondspeciesother...312`
## • `secondspeciespicture` -> `secondspeciespicture...313`
## • `secondspeciespictureURL` -> `secondspeciespictureURL...314`
## • `greencoverpercentageharvest` -> `greencoverpercentageharvest...315`
## • `greencoverpicture` -> `greencoverpicture...316`
## • `greencoverpictureURL` -> `greencoverpictureURL...317`
## • `questionswantmention` -> `questionswantmention...323`
## • `howmuchreduceyield` -> `howmuchreduceyield...325`
## • `maize_ green_ biomass_ total_ weight_ at_ harvest` -> `maize_ green_
##   biomass_ total_ weight_ at_ harvest...385`
## • `maize_ green_ biomass_ total_ weight_ at_ harvest` -> `maize_ green_
##   biomass_ total_ weight_ at_ harvest...388`
## • `questionswantmention` -> `questionswantmention...407`
## • `howmuchreduceyield` -> `howmuchreduceyield...408`
yield_CA2_25B <- read_excel(
  file.path(data_raw, "CA3 data.xlsx"),
  sheet = " 25B CA3"
)
## New names:
## • `environment` -> `environment...3`
## • `Spacing_btwn_rows_cm` -> `Spacing_btwn_rows_cm...31`
## • `Spacing_btwn_rows_cm` -> `Spacing_btwn_rows_cm...32`
## • `spacing_between_holes` -> `spacing_between_holes...33`
## • `spacing_between_holes` -> `spacing_between_holes...34`
## • `land_prep_sex` -> `land_prep_sex...41`
## • `land_prep_sex` -> `land_prep_sex...54`
## • `urea_application_period` -> `urea_application_period...76`
## • `topdress_grams_used` -> `topdress_grams_used...77`
## • `topdress_application_date` -> `topdress_application_date...78`
## • `topdress_green_cover_percentage` -> `topdress_green_cover_percentage...79`
## • `labor_start_topdress` -> `labor_start_topdress...80`
## • `labor_end_topdress` -> `labor_end_topdress...81`
## • `topdress_minutes` -> `topdress_minutes...82`
## • `labor_start_binnage` -> `labor_start_binnage...90`
## • `labor_end_binnage` -> `labor_end_binnage...91`
## • `binnage_minutes` -> `binnage_minutes...92`
## • `binnage_gender` -> `binnage_gender...93`
## • `binnage_easy` -> `binnage_easy...95`
## • `binnage_easy_compared_CT` -> `binnage_easy_compared_CT...96`
## • `binnage_recommandation` -> `binnage_recommandation...97`
## • `binnage_recommandation_explaination` ->
##   `binnage_recommandation_explaination...98`
## • `binnage_recommandation_marks` -> `binnage_recommandation_marks...99`
## • `binnage_recommandation_marks_explaination` ->
##   `binnage_recommandation_marks_explaination...100`
## • `binnage_complication_observed` -> `binnage_complication_observed...101`
## • `Alternative_weedrec_ethod_by_farmer` ->
##   `Alternative_weedrec_ethod_by_farmer...102`
## • `binnage_done_differently` -> `binnage_done_differently...103`
## • `binnage2_done...401` -> `binnage2_done`
## • `binnage2_date_...405` -> `binnage2_date_...105`
## • `binnage2_io_present_...409` -> `binnage2_io_present_...106`
## • `binnage2_method_...413` -> `binnage2_method_...107`
## • `labor_start_binnage` -> `labor_start_binnage...108`
## • `labor_end_binnage` -> `labor_end_binnage...109`
## • `binnage_minutes` -> `binnage_minutes...110`
## • `binnage_gender` -> `binnage_gender...111`
## • `binnage_easy` -> `binnage_easy...113`
## • `binnage_easy_compared_CT` -> `binnage_easy_compared_CT...114`
## • `binnage_recommandation` -> `binnage_recommandation...115`
## • `binnage_recommandation_explaination` ->
##   `binnage_recommandation_explaination...116`
## • `binnage_recommandation_marks` -> `binnage_recommandation_marks...117`
## • `binnage_recommandation_marks_explaination` ->
##   `binnage_recommandation_marks_explaination...118`
## • `binnage_complication_observed` -> `binnage_complication_observed...119`
## • `binnage_done_differently` -> `binnage_done_differently...120`
## • `environment` -> `environment...121`
## • `binnage2_done_...569` -> `binnage2_done_`
## • `binnage2_date_...573` -> `binnage2_date_...123`
## • `binnage2_io_present_...577` -> `binnage2_io_present_...124`
## • `binnage2_method_...581` -> `binnage2_method_...125`
## • `Alternative_weedrec_ethod_by_farmer` ->
##   `Alternative_weedrec_ethod_by_farmer...126`
## • `binnage3_done_...590` -> `binnage3_done_...127`
## • `binnage3_date_...594` -> `binnage3_date_`
## • `binnage3_method_...602` -> `binnage3_method_...130`
## • `labor_start_binnage` -> `labor_start_binnage...131`
## • `labor_end_binnage` -> `labor_end_binnage...132`
## • `binnage_minutes` -> `binnage_minutes...133`
## • `binnage_gender` -> `binnage_gender...134`
## • `binnage_easy` -> `binnage_easy...136`
## • `binnage_easy_compared_CT` -> `binnage_easy_compared_CT...137`
## • `binnage_recommandation` -> `binnage_recommandation...138`
## • `binnage_recommandation_explaination` ->
##   `binnage_recommandation_explaination...139`
## • `binnage_recommandation_marks` -> `binnage_recommandation_marks...140`
## • `binnage_recommandation_marks_explaination` ->
##   `binnage_recommandation_marks_explaination...141`
## • `binnage_complication_observed` -> `binnage_complication_observed...142`
## • `binnage_done_differently` -> `binnage_done_differently...143`
## • `binnage3_done_...758` -> `binnage3_done_...145`
## • `binnage3_io_present_...766` -> `binnage3_io_present_`
## • `binnage3_method_...770` -> `binnage3_method_...148`
## • `Alternative_weedrec_ethod_by_farmer` ->
##   `Alternative_weedrec_ethod_by_farmer...149`
## • `urea_application_period` -> `urea_application_period...150`
## • `topdress_grams_used` -> `topdress_grams_used...151`
## • `topdress_application_date` -> `topdress_application_date...152`
## • `topdress_green_cover_percentage` -> `topdress_green_cover_percentage...153`
## • `labor_start_topdress` -> `labor_start_topdress...154`
## • `labor_end_topdress` -> `labor_end_topdress...155`
## • `topdress_minutes` -> `topdress_minutes...156`
## • `date_recording_disease` -> `date_recording_disease...166`
## • `beans_anthracnose_incidence` -> `beans_anthracnose_incidence...167`
## • `beans_anthracnose_severity` -> `beans_anthracnose_severity...168`
## • `beans_anthracnose_plantcount` -> `beans_anthracnose_plantcount...169`
## • `angular_leaf_spot_present` -> `angular_leaf_spot_present...170`
## • `desease_name_angular_leaf_spot` -> `desease_name_angular_leaf_spot...171`
## • `angular_leaf_spot_incidence` -> `angular_leaf_spot_incidence...173`
## • `angular_leaf_spot_severity` -> `angular_leaf_spot_severity...174`
## • `angular_leaf_spot_plantcount` -> `angular_leaf_spot_plantcount...175`
## • `beans_anthracnose_incidence` -> `beans_anthracnose_incidence...199`
## • `beans_anthracnose_severity` -> `beans_anthracnose_severity...200`
## • `beans_anthracnose_plantcount` -> `beans_anthracnose_plantcount...201`
## • `angular_leaf_spot_present` -> `angular_leaf_spot_present...202`
## • `desease_name_angular_leaf_spot` -> `desease_name_angular_leaf_spot...203`
## • `angular_leaf_spot_incidence` -> `angular_leaf_spot_incidence...204`
## • `angular_leaf_spot_severity` -> `angular_leaf_spot_severity...205`
## • `angular_leaf_spot_plantcount` -> `angular_leaf_spot_plantcount...206`
## • `date_recording_disease` -> `date_recording_disease...207`
## • `rust_recording-date` -> `rust_recording-date...240`
## • `gls_severity` -> `gls_severity...241`
## • `gls_recording-date` -> `gls_recording-date...242`
## • `rust_present` -> `rust_present...243`
## • `rust_Incidence` -> `rust_Incidence...244`
## • `rust_severity` -> `rust_severity...245`
## • `tlb_recording-date` -> `tlb_recording-date...249`
## • `gls_severity` -> `gls_severity...252`
## • `rust_present` -> `rust_present...253`
## • `rust_Incidence` -> `rust_Incidence...254`
## • `rust_severity` -> `rust_severity...255`
## • `rust_recording-date` -> `rust_recording-date...256`
## • `gls_recording-date` -> `gls_recording-date...257`
## • `tlb_recording-date` -> `tlb_recording-date...258`
## • `living_much_day` -> `living_much_day...259`
## • `weeds_reduce_harvest` -> `weeds_reduce_harvest...260`
## • `weed_first_species` -> `weed_first_species...261`
## • `other_main_species` -> `other_main_species...262`
## • `first_species_picture` -> `first_species_picture...263`
## • `weed_second_species` -> `weed_second_species...265`
## • `second_species_other` -> `second_species_other...266`
## • `second_species_picture` -> `second_species_picture...267`
## • `green_cover_percentage` -> `green_cover_percentage...269`
## • `Green_cover_picture` -> `Green_cover_picture...270`
## • `living_much_day` -> `living_much_day...272`
## • `weeds_reduce_harvest` -> `weeds_reduce_harvest...273`
## • `weed_first_species` -> `weed_first_species...274`
## • `other_main_species` -> `other_main_species...275`
## • `first_species_picture` -> `first_species_picture...276`
## • `weed_second_species` -> `weed_second_species...278`
## • `second_species_other` -> `second_species_other...279`
## • `second_species_picture` -> `second_species_picture...280`
## • `green_cover_percentage` -> `green_cover_percentage...282`
## • `Green_cover_picture` -> `Green_cover_picture...283`
## • `questions_want_mention` -> `questions_want_mention...287`
## • `questions_affect_control` -> `questions_affect_control...288`
## • `Officer_do_you_thin_affect_control` ->
##   `Officer_do_you_thin_affect_control...289`
## • `How_much_it_will_reduce_yield` -> `How_much_it_will_reduce_yield...290`
## • `Harvest_box_length_m` -> `Harvest_box_length_m...293`
## • `Harvest_box_width_m` -> `Harvest_box_width_m...294`
## • `Harvest_box_size_m2` -> `Harvest_box_size_m2...295`
## • `residues_mulch_reduced_season` -> `residues_mulch_reduced_season...304`
## • `residues_mulch_reduced_reason_harvest` ->
##   `residues_mulch_reduced_reason_harvest...305`
## • `others_reasons_ask_farm` -> `others_reasons_ask_farm...306`
## • `questions_want_mention` -> `questions_want_mention...307`
## • `questions_affect_control` -> `questions_affect_control...308`
## • `how_much_reduce_yield` -> `how_much_reduce_yield...309`
## • `revenue_possible_harvest_box` -> `revenue_possible_harvest_box...311`
## • `beans_green_biomass_total_weig` -> `beans_green_biomass_total_weig...314`
## • `beans_green_biomass_sample_weight` ->
##   `beans_green_biomass_sample_weight...318`
## • `beans_green_biomass_sample_code` -> `beans_green_biomass_sample_code...319`
## • `harvest_methods_recommadation` -> `harvest_methods_recommadation...320`
## • `harvest_method_recomendation_reasons` ->
##   `harvest_method_recomendation_reasons...321`
## • `Moisture_content` -> `Moisture_content...324`
## • `residues_mulch_reduced_season` -> `residues_mulch_reduced_season...325`
## • `residues_mulch_reduced_reason_harvest` ->
##   `residues_mulch_reduced_reason_harvest...326`
## • `others_reasons_ask_farm` -> `others_reasons_ask_farm...327`
## • `questions_want_mention` -> `questions_want_mention...328`
## • `questions_affect_control` -> `questions_affect_control...329`
## • `how_much_reduce_yield` -> `how_much_reduce_yield...330`
## • `revenue_possible_harvest_box` -> `revenue_possible_harvest_box...331`
## • `beans_green_biomass_total_weig` -> `beans_green_biomass_total_weig...332`
## • `beans_green_biomass_total_weight` ->
##   `beans_green_biomass_total_weight...334`
## • `beans_green_biomass_total_weight` ->
##   `beans_green_biomass_total_weight...336`
## • `beans_green_biomass_sample_weight` ->
##   `beans_green_biomass_sample_weight...338`
## • `beans_green_biomass_sample_code` -> `beans_green_biomass_sample_code...339`
## • `harvest_methods_recommadation` -> `harvest_methods_recommadation...340`
## • `harvest_method_recomendation_reasons` ->
##   `harvest_method_recomendation_reasons...341`
## • `Officer_do_you_thin_affect_control` ->
##   `Officer_do_you_thin_affect_control...372`
## • `How_much_it_will_reduce_yield` -> `How_much_it_will_reduce_yield...373`
## • `Harvest_box_length_m` -> `Harvest_box_length_m...375`
## • `Harvest_box_width_m` -> `Harvest_box_width_m...376`
## • `Harvest_box_size_m2` -> `Harvest_box_size_m2...377`
## • `Moisture_content` -> `Moisture_content...400`
yield_CA2_26A <- read_excel(
  file.path(data_raw, "CA3 data.xlsx"),
  sheet = "26A CA3"
)
## New names:
## • `q085b_prob1_happened...340` -> `q085b_prob1_happened...336`
## • `q084d_prob2_yield_reduction...351` -> `q084d_prob2_yield_reduction...347`
## • `q085b_prob1_happened...549` -> `q085b_prob1_happened...525`
## • `q084d_prob2_yield_reduction...551` -> `q084d_prob2_yield_reduction...527`

— Balance check in old CA trial

We firs merge databases

library(dplyr)
## Warning: package 'dplyr' was built under R version 4.3.3
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
# --- 1. Confirm column names exist ---
cols_wanted_sel <- c("District", "Agzone", "Officer", "Cell", "Block", "Quali")
cols_wanted_yield <- c("IQR_Season", "IQF_environment", "IQR_sex_HH_head", "Block",
                       "ICF_Lat", "ICF_Long", "ICT_Alt", "Y_ratio", "env_index",
                       "yield_A", "yield_C", "IQF_position_slope", "Erosion_quanti", "Slope")

cat("Missing in selection_CA1:\n")
## Missing in selection_CA1:
print(cols_wanted_sel[!cols_wanted_sel %in% names(selection_CA1)])
## [1] "Cell"
cat("\nMissing in yield_CA1:\n")
## 
## Missing in yield_CA1:
print(cols_wanted_yield[!cols_wanted_yield %in% names(yield_CA1)])
## [1] "ICT_Alt"
# --- 2. Compare Block values between the two datasets ---
blocks_sel   <- unique(selection_CA1$Block)
blocks_yield <- unique(yield_CA1$Block)

cat("\nNumber of unique Blocks in selection_CA1:", length(blocks_sel), "\n")
## 
## Number of unique Blocks in selection_CA1: 266
cat("Number of unique Blocks in yield_CA1:", length(blocks_yield), "\n")
## Number of unique Blocks in yield_CA1: 565
# Blocks listed in selection_CA1 but NOT found anywhere in yield_CA1 --> PROBLEM
orphan_blocks <- setdiff(blocks_sel, blocks_yield)

cat("\nNumber of Blocks in selection_CA1 with NO match in yield_CA1:", length(orphan_blocks), "\n")
## 
## Number of Blocks in selection_CA1 with NO match in yield_CA1: 3
if (length(orphan_blocks) > 0) {
  cat("These Blocks are missing from yield_CA1:\n")
  print(orphan_blocks)
}
## These Blocks are missing from yield_CA1:
## [1] "48_10" "115_4" "96_16"
# Optional: view the actual selection_CA1 rows that are orphaned, for context
orphan_rows <- selection_CA1 %>% filter(Block %in% orphan_blocks)
print(orphan_rows)
## # A tibble: 3 × 22
##   Season Region...2 Agzone District Officer cell  `Trial number` farmer_id Block
##   <chr>  <chr>      <chr>  <chr>    <chr>   <chr>          <dbl>     <dbl> <chr>
## 1 26B    NW         Volca… Burera   Uwimana Nkum…             48        10 48_10
## 2 26B    SW         Cyang… Rusizi   Vedaste Gahi…            115         4 115_4
## 3 26B    W          Lake … Nyamash… Joseli… Jara…             96        16 96_16
## # ℹ 13 more variables: Farmer_name <chr>, village <chr>, phone <dbl>,
## #   Quali <chr>, ...14 <lgl>, ...15 <lgl>, ...16 <lgl>, Category...17 <chr>,
## #   `Nbr of farmers...18` <dbl>, ...19 <dbl>, Region...20 <chr>,
## #   Category...21 <chr>, `Nbr of farmers...22` <dbl>
library(dplyr)

# --- Build check_CA1 ---

# Subset selection_CA1 (note: lowercase "cell")
sel_subset <- selection_CA1 %>%
  select(District, Agzone, Officer, cell, Block, Quali)

# Subset yield_CA1 (note: ICF_Alt, not ICT_Alt)
yield_subset <- yield_CA1 %>%
  select(Block, IQR_Season, IQF_environment, IQR_sex_HH_head,
         ICF_Lat, ICF_Long, ICF_Alt, Y_ratio, env_index, yield_A, yield_C)

# Left join: keep ALL Blocks from selection_CA1, even if no match in yield_CA1
# This naturally keeps all seasons per Block where they exist
check_CA1 <- sel_subset %>%
  left_join(yield_subset, by = "Block") %>%
  mutate(
    data_flag = ifelse(is.na(IQR_Season), "no_yield_data_found", "ok")
  )
## Warning in left_join(., yield_subset, by = "Block"): Detected an unexpected many-to-many relationship between `x` and `y`.
## ℹ Row 1 of `x` matches multiple rows in `y`.
## ℹ Row 1855 of `y` matches multiple rows in `x`.
## ℹ If a many-to-many relationship is expected, set `relationship =
##   "many-to-many"` to silence this warning.
# --- Sanity checks ---
cat("Total rows in check_CA1:", nrow(check_CA1), "\n")
## Total rows in check_CA1: 1527
cat("Unique Blocks in check_CA1:", length(unique(check_CA1$Block)), "\n")
## Unique Blocks in check_CA1: 266
cat("Rows flagged as missing yield data:", sum(check_CA1$data_flag == "no_yield_data_found"), "\n\n")
## Rows flagged as missing yield data: 3
# View the flagged/orphan rows specifically
check_CA1 %>% filter(data_flag == "no_yield_data_found")
## # A tibble: 3 × 17
##   District   Agzone         Officer cell  Block Quali IQR_Season IQF_environment
##   <chr>      <chr>          <chr>   <chr> <chr> <chr> <chr>      <chr>          
## 1 Burera     Volcanic cones Uwimana Nkum… 48_10 Very… <NA>       <NA>           
## 2 Rusizi     Cyangugu       Vedaste Gahi… 115_4 Very… <NA>       <NA>           
## 3 Nyamasheke Lake Kivu      Joseli… Jara… 96_16 Very… <NA>       <NA>           
## # ℹ 9 more variables: IQR_sex_HH_head <chr>, ICF_Lat <dbl>, ICF_Long <dbl>,
## #   ICF_Alt <dbl>, Y_ratio <dbl>, env_index <dbl>, yield_A <dbl>,
## #   yield_C <dbl>, data_flag <chr>
# How many seasons per Block did we retain (for the non-orphan Blocks)?
check_CA1 %>%
  filter(data_flag == "ok") %>%
  count(Block, name = "n_seasons") %>%
  arrange(desc(n_seasons)) %>%
  head(10)
## # A tibble: 10 × 2
##    Block  n_seasons
##    <chr>      <int>
##  1 16_15         12
##  2 22_2          12
##  3 17_2           8
##  4 102_10         6
##  5 102_11         6
##  6 102_12         6
##  7 102_13         6
##  8 102_14         6
##  9 102_2          6
## 10 102_5          6

Then explore the balce in terms of yield

library(dplyr)
library(tidyr)
library(ggplot2)

# --- Filter out orphan/no-data rows ---
check_CA1_clean <- check_CA1 %>%
  filter(data_flag != "no_yield_data_found")

# --- Order Quali levels logically ---
check_CA1_clean <- check_CA1_clean %>%
  mutate(Quali = factor(Quali, levels = c("Very Low", "Low", "Medium", "High", "Very High")))

# --- Summary table: mean Y_ratio, yield_A, yield_C by Quali x Season ---
summary_table <- check_CA1_clean %>%
  group_by(Quali, IQR_Season) %>%
  summarise(
    n         = n(),
    mean_Y_ratio = mean(Y_ratio, na.rm = TRUE),
    mean_yield_A = mean(yield_A, na.rm = TRUE),
    mean_yield_C = mean(yield_C, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  arrange(IQR_Season, Quali)

print(summary_table)
## # A tibble: 30 × 6
##    Quali     IQR_Season     n mean_Y_ratio mean_yield_A mean_yield_C
##    <fct>     <chr>      <int>        <dbl>        <dbl>        <dbl>
##  1 Very Low  23A           65        0.932         4.44        3.98 
##  2 Low       23A           11        0.840         5.59        4.57 
##  3 Medium    23A           39        0.975         5.22        4.82 
##  4 High      23A            5        0.913         5.39        4.93 
##  5 Very High 23A          133        0.919         5.27        4.70 
##  6 Very Low  23B           64        0.780         1.39        1.01 
##  7 Low       23B           11        0.971         1.28        0.989
##  8 Medium    23B           41        0.867         1.38        1.18 
##  9 High      23B            5        0.729         1.63        1.26 
## 10 Very High 23B          139        0.763         1.75        1.28 
## # ℹ 20 more rows
# Optional: save table to outputs folder
write.csv(summary_table, file.path(output_dir, "summary_Quali_Season.csv"), row.names = FALSE)
library(dplyr)
library(ggplot2)
library(multcompView)

# --- Function: run ANOVA + Tukey HSD per season, return letters ---
get_letters <- function(df) {
  model <- aov(Y_ratio ~ Quali, data = df)
  tukey <- TukeyHSD(model)
  letters_df <- multcompLetters4(model, tukey)$Quali$Letters
  data.frame(Quali = names(letters_df), letter = letters_df, row.names = NULL)
}

# --- Letters per season ---
letters_by_season <- check_CA1_clean %>%
  filter(!is.na(Y_ratio)) %>%
  group_by(IQR_Season) %>%
  group_modify(~ get_letters(.x)) %>%
  ungroup()

# --- N, mean, and y-position per Quali x Season ---
label_stats <- check_CA1_clean %>%
  filter(!is.na(Y_ratio)) %>%
  group_by(IQR_Season, Quali) %>%
  summarise(
    n      = n(),
    mean_v = mean(Y_ratio, na.rm = TRUE),
    y_max  = max(Y_ratio, na.rm = TRUE),
    .groups = "drop"
  )

# --- Combine letters + stats into one label, stacked with line breaks ---
label_data <- label_stats %>%
  left_join(letters_by_season, by = c("IQR_Season", "Quali")) %>%
  mutate(
    label = paste0(letter, "\nn=", n, "\nmean=", round(mean_v, 2)),
    y_pos = y_max * 1.05
  )

# --- Final plot ---
p_yratio <- ggplot(check_CA1_clean %>% filter(!is.na(Y_ratio)),
                    aes(x = Quali, y = Y_ratio, fill = Quali)) +
  geom_boxplot(na.rm = TRUE) +
  facet_wrap(~ IQR_Season) +
  geom_text(data = label_data,
            aes(x = Quali, y = y_pos, label = label),
            inherit.aes = FALSE, vjust = 0, size = 3, fontface = "bold", lineheight = 0.85) +
  labs(title = "Y_ratio by Quali, faceted by Season (letters = Tukey HSD groups)",
       x = "Quali", y = "Y_ratio") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), legend.position = "none") +
  scale_y_continuous(expand = expansion(mult = c(0.05, 0.2)))  # extra headroom for labels

print(p_yratio)

ggsave(file.path(output_dir, "boxplot_Y_ratio_by_Quali_Season_letters.png"), p_yratio, width = 9, height = 6)

across seasons

library(dplyr)
library(ggplot2)
library(multcompView)

# --- Step 1: average Y_ratio per Block across seasons ---
block_avg <- check_CA1_clean %>%
  group_by(Block, District, Agzone, Officer, cell, Quali) %>%
  summarise(
    n_seasons    = n(),
    mean_Y_ratio = mean(Y_ratio, na.rm = TRUE),
    .groups = "drop"
  )

cat("Number of Blocks after averaging:", nrow(block_avg), "\n")
## Number of Blocks after averaging: 264
head(block_avg)
## # A tibble: 6 × 8
##   Block  District Agzone   Officer cell       Quali     n_seasons mean_Y_ratio
##   <chr>  <chr>    <chr>    <chr>   <chr>      <fct>         <int>        <dbl>
## 1 102_10 Bugarama Bugarama Wellars Hangabashi Very Low          6        0.647
## 2 102_11 Bugarama Bugarama Wellars Hangabashi Very Low          6        0.996
## 3 102_12 Bugarama Bugarama Wellars Hangabashi Very High         6        0.658
## 4 102_13 Bugarama Bugarama Wellars Hangabashi Very Low          6        0.890
## 5 102_14 Bugarama Bugarama Wellars Hangabashi Very High         6        1.12 
## 6 102_2  Bugarama Bugarama Wellars Hangabashi Very High         6        0.764
# --- Step 2: Tukey HSD across Quali groups (no season facet now) ---
model <- aov(mean_Y_ratio ~ Quali, data = block_avg)
tukey <- TukeyHSD(model)
letters_df <- multcompLetters4(model, tukey)$Quali$Letters
letters_tbl <- data.frame(Quali = names(letters_df), letter = letters_df, row.names = NULL)

# --- Step 3: N, mean, y-position per Quali group ---
label_stats <- block_avg %>%
  group_by(Quali) %>%
  summarise(
    n      = n(),
    mean_v = mean(mean_Y_ratio, na.rm = TRUE),
    y_max  = max(mean_Y_ratio, na.rm = TRUE),
    .groups = "drop"
  )

label_data <- label_stats %>%
  left_join(letters_tbl, by = "Quali") %>%
  mutate(
    label = paste0(letter, "\nn=", n, "\nmean=", round(mean_v, 2)),
    y_pos = y_max * 1.05
  )

# --- Step 4: Plot ---
library(dplyr)
library(ggplot2)
library(multcompView)

p_block_avg <- ggplot(block_avg, aes(x = Quali, y = mean_Y_ratio, fill = Quali)) +
  geom_boxplot(na.rm = TRUE, outlier.shape = NA, alpha = 0.6) +  # outlier.shape = NA avoids double-plotting outliers
  geom_jitter(width = 0.15, height = 0, alpha = 0.6, size = 1.8, color = "black") +
  geom_text(data = label_data,
            aes(x = Quali, y = y_pos, label = label),
            inherit.aes = FALSE, vjust = 0, size = 3, fontface = "bold", lineheight = 0.85) +
  labs(title = "Average Y_ratio per Block (across seasons) by Quali",
       x = "Quality of the trial", y = "Mean Y_ratio (per Block)") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), legend.position = "none") +
  scale_y_continuous(expand = expansion(mult = c(0.05, 0.2)))

print(p_block_avg)

ggsave(file.path(output_dir, "boxplot_Y_ratio_by_Block_avg_Quali_dots.png"), p_block_avg, width = 8, height = 6)

Effect of selection of slope

library(dplyr)
library(ggplot2)
library(multcompView)

# --- Step 0: rebuild check_CA1 to include Slope ---
yield_subset <- yield_CA1 %>%
  select(Block, IQR_Season, IQF_environment, IQR_sex_HH_head,
         ICF_Lat, ICF_Long, ICF_Alt, Y_ratio, env_index, yield_A, yield_C, Slope)

check_CA1 <- sel_subset %>%
  left_join(yield_subset, by = "Block") %>%
  mutate(
    data_flag = ifelse(is.na(IQR_Season), "no_yield_data_found", "ok")
  )
## Warning in left_join(., yield_subset, by = "Block"): Detected an unexpected many-to-many relationship between `x` and `y`.
## ℹ Row 1 of `x` matches multiple rows in `y`.
## ℹ Row 1855 of `y` matches multiple rows in `x`.
## ℹ If a many-to-many relationship is expected, set `relationship =
##   "many-to-many"` to silence this warning.
check_CA1_clean <- check_CA1 %>%
  filter(data_flag != "no_yield_data_found") %>%
  mutate(Quali = factor(Quali, levels = c("Very Low", "Low", "Medium", "High", "Very High")))

# --- Step 1: average Slope per Block across seasons ---
block_avg_slope <- check_CA1_clean %>%
  group_by(Block, District, Agzone, Officer, cell, Quali) %>%
  summarise(
    n_seasons  = n(),
    mean_Slope = mean(Slope, na.rm = TRUE),
    .groups = "drop"
  )

cat("Number of Blocks after averaging:", nrow(block_avg_slope), "\n")
## Number of Blocks after averaging: 264
head(block_avg_slope)
## # A tibble: 6 × 8
##   Block  District Agzone   Officer cell       Quali     n_seasons mean_Slope
##   <chr>  <chr>    <chr>    <chr>   <chr>      <fct>         <int>      <dbl>
## 1 102_10 Bugarama Bugarama Wellars Hangabashi Very Low          6         15
## 2 102_11 Bugarama Bugarama Wellars Hangabashi Very Low          6         15
## 3 102_12 Bugarama Bugarama Wellars Hangabashi Very High         6         15
## 4 102_13 Bugarama Bugarama Wellars Hangabashi Very Low          6         20
## 5 102_14 Bugarama Bugarama Wellars Hangabashi Very High         6         12
## 6 102_2  Bugarama Bugarama Wellars Hangabashi Very High         6         14
# --- Step 2: Tukey HSD across Quali groups ---
model <- aov(mean_Slope ~ Quali, data = block_avg_slope)
tukey <- TukeyHSD(model)
letters_df <- multcompLetters4(model, tukey)$Quali$Letters
letters_tbl <- data.frame(Quali = names(letters_df), letter = letters_df, row.names = NULL)

# --- Step 3: N, mean, y-position per Quali group ---
label_stats <- block_avg_slope %>%
  group_by(Quali) %>%
  summarise(
    n      = n(),
    mean_v = mean(mean_Slope, na.rm = TRUE),
    y_max  = max(mean_Slope, na.rm = TRUE),
    .groups = "drop"
  )

label_data <- label_stats %>%
  left_join(letters_tbl, by = "Quali") %>%
  mutate(
    label = paste0(letter, "\nn=", n, "\nmean=", round(mean_v, 2)),
    y_pos = y_max * 1.05
  )

# --- Step 4: Plot ---
p_block_avg_slope <- ggplot(block_avg_slope, aes(x = Quali, y = mean_Slope, fill = Quali)) +
  geom_boxplot(na.rm = TRUE, outlier.shape = NA, alpha = 0.6) +
  geom_jitter(width = 0.15, height = 0, alpha = 0.6, size = 1.8, color = "black") +
  geom_text(data = label_data,
            aes(x = Quali, y = y_pos, label = label),
            inherit.aes = FALSE, vjust = 0, size = 3, fontface = "bold", lineheight = 0.85) +
  labs(title = "Average Slope per Block (across seasons) by Quali",
       x = "Quality of the trial", y = "Mean Slope (per Block)") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), legend.position = "none") +
  scale_y_continuous(expand = expansion(mult = c(0.05, 0.2)))

print(p_block_avg_slope)

ggsave(file.path(output_dir, "boxplot_Slope_by_Block_avg_Quali_dots.png"), p_block_avg_slope, width = 8, height = 6)

Effect on position on th eslope?

library(dplyr)
library(ggplot2)
library(tidyr)

# --- Step 0: add IQF_position_slope into check_CA1 ---
yield_subset <- yield_CA1 %>%
  select(Block, IQR_Season, IQF_environment, IQR_sex_HH_head,
         ICF_Lat, ICF_Long, ICF_Alt, Y_ratio, env_index, yield_A, yield_C,
         Slope, IQF_position_slope)

check_CA1 <- sel_subset %>%
  left_join(yield_subset, by = "Block") %>%
  mutate(
    data_flag = ifelse(is.na(IQR_Season), "no_yield_data_found", "ok"),
    # clean junk codes into real NA
    IQF_position_slope = na_if(IQF_position_slope, "---"),
    IQF_position_slope = na_if(IQF_position_slope, "#N/A")
  )
## Warning in left_join(., yield_subset, by = "Block"): Detected an unexpected many-to-many relationship between `x` and `y`.
## ℹ Row 1 of `x` matches multiple rows in `y`.
## ℹ Row 1855 of `y` matches multiple rows in `x`.
## ℹ If a many-to-many relationship is expected, set `relationship =
##   "many-to-many"` to silence this warning.
check_CA1_clean <- check_CA1 %>%
  filter(data_flag != "no_yield_data_found") %>%
  mutate(Quali = factor(Quali, levels = c("Very Low", "Low", "Medium", "High", "Very High")))

# --- Step 1: most common slope-position per Block across seasons ---
get_mode <- function(x) {
  x <- x[!is.na(x)]
  if (length(x) == 0) return(NA_character_)
  names(sort(table(x), decreasing = TRUE))[1]
}

block_slope_pos <- check_CA1_clean %>%
  group_by(Block, District, Agzone, Officer, cell, Quali) %>%
  summarise(
    n_seasons     = n(),
    slope_pos_mode = get_mode(IQF_position_slope),
    .groups = "drop"
  ) %>%
  filter(!is.na(slope_pos_mode))

cat("Number of Blocks with slope-position data:", nrow(block_slope_pos), "\n")
## Number of Blocks with slope-position data: 261
# --- Step 2: Chi-square test of independence (Quali x slope position) ---
cont_table <- table(block_slope_pos$Quali, block_slope_pos$slope_pos_mode)
print(cont_table)
##            
##             Backslope Flat Footslope Shoulder Summit
##   Very Low         45    3         9        2      7
##   Low               6    0         0        3      1
##   Medium           26    3         2        5      3
##   High              4    0         0        1      0
##   Very High        93   11        18       16      3
chisq_result <- chisq.test(cont_table)
## Warning in chisq.test(cont_table): Chi-squared approximation may be incorrect
print(chisq_result)
## 
##  Pearson's Chi-squared test
## 
## data:  cont_table
## X-squared = 20.858, df = 16, p-value = 0.184
# --- Step 3: proportions for plotting ---
prop_data <- block_slope_pos %>%
  count(Quali, slope_pos_mode) %>%
  group_by(Quali) %>%
  mutate(prop = n / sum(n)) %>%
  ungroup()

# --- Step 4: stacked bar chart ---
p_slope_pos <- ggplot(prop_data, aes(x = Quali, y = prop, fill = slope_pos_mode)) +
  geom_col(position = "stack", color = "white") +
  geom_text(aes(label = ifelse(prop > 0.04, paste0(round(prop*100), "%"), "")),
            position = position_stack(vjust = 0.5), size = 3, color = "black") +
  labs(title = paste0("Slope position by Quali (Chi-sq p = ", 
                       signif(chisq_result$p.value, 3), ")"),
       x = "Quality of the trial", y = "Proportion of Blocks", fill = "Slope position") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

print(p_slope_pos)

ggsave(file.path(output_dir, "barplot_slopeposition_by_Quali.png"), p_slope_pos, width = 8, height = 6)

Map of plot sdistribution

library(dplyr)
library(ggplot2)


# --- Step 1: one Lat/Long per Block ---
block_coords <- check_CA1_clean %>%
  group_by(Block, Quali) %>%
  summarise(
    Lat  = mean(ICF_Lat, na.rm = TRUE),
    Long = mean(ICF_Long, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  filter(!is.na(Lat), !is.na(Long))

cat("Number of Blocks with coordinates:", nrow(block_coords), "\n")
## Number of Blocks with coordinates: 264
# --- Step 2: get Rwanda country boundary ---
# install.packages("rnaturalearth")  # run once if not installed
# install.packages("sf")             # run once if not installed
# install.packages("geodata")  # run once if not installed
library(geodata)
## Loading required package: terra
## Warning: package 'terra' was built under R version 4.3.3
## terra 1.8.29
## 
## Attaching package: 'terra'
## The following object is masked from 'package:tidyr':
## 
##     extract
library(sf)
## Warning: package 'sf' was built under R version 4.3.3
## Linking to GEOS 3.11.2, GDAL 3.8.2, PROJ 9.3.1; sf_use_s2() is TRUE
library(dplyr)
library(ggplot2)

# --- Step 1: Rwanda district boundaries ---
rwanda_district <- gadm(country = "RWA", level = 2, path = tempdir()) %>% st_as_sf()

# --- Step 2: Block coordinates ---
block_coords <- check_CA1_clean %>%
  group_by(Block, Quali) %>%
  summarise(
    Lat  = mean(ICF_Lat, na.rm = TRUE),
    Long = mean(ICF_Long, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  filter(!is.na(Lat), !is.na(Long)) %>%
  mutate(Quali = factor(Quali, levels = c("Very Low", "Low", "Medium", "High", "Very High")))

# --- Step 3: Plot with district boundaries + smaller, jittered points ---
set.seed(123)  # for reproducible jitter placement

p_map <- ggplot() +
  geom_sf(data = rwanda_district, fill = "gray97", color = "black", linewidth = 0.5) +
  geom_jitter(data = block_coords, aes(x = Long, y = Lat, color = Quali),
              width = 0.02, height = 0.02, size = 1.2, alpha = 0.8) +
  scale_color_manual(values = c("Very Low" = "#d73027", "Low" = "#fc8d59",
                                 "Medium" = "#fee08b", "High" = "#91cf60",
                                 "Very High" = "#1a9850")) +
  coord_sf(xlim = c(28.8, 30.9), ylim = c(-2.85, -1.0), expand = FALSE) +
  labs(title = "Farm Blocks by Quali, with District boundaries",
       x = NULL, y = NULL, color = "Quali") +
  theme_minimal() +
  theme(panel.grid = element_blank())

print(p_map)

ggsave(file.path(output_dir, "map_Blocks_Quali_district_boundaries.png"), p_map, width = 10, height = 10, dpi = 300)