Prepare the data

Your data should look like this:

library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.2.1     ✔ readr     2.2.0
## ✔ forcats   1.0.1     ✔ stringr   1.6.0
## ✔ ggplot2   4.0.3     ✔ tibble    3.3.1
## ✔ lubridate 1.9.5     ✔ tidyr     1.3.2
## ✔ purrr     1.2.2     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
od <- tribble(
  ~Origin, ~Destination, ~Volume,
  "Olopi", "Kilgoris", 1320,
  "Olopi", "Kehancha", 2845,
  "Kilgoris", "Olopi", 1180,
  "Kilgoris", "Kehancha", 2010,
  "Kehancha", "Olopi", 2560,
  "Kehancha", "Kilgoris", 1715
)
  1. Generate an O-D Matrix
od_matrix <- od %>%
  pivot_wider(
    names_from = Destination,
    values_from = Volume,
    values_fill = 0
  )

od_matrix
## # A tibble: 3 × 4
##   Origin   Kilgoris Kehancha Olopi
##   <chr>       <dbl>    <dbl> <dbl>
## 1 Olopi        1320     2845     0
## 2 Kilgoris        0     2010  1180
## 3 Kehancha     1715        0  2560
  1. Total Departures (Origin Totals)
origin_summary <- od %>%
  group_by(Origin) %>%
  summarise(
    Total_Outbound = sum(Volume)
  )

origin_summary
## # A tibble: 3 × 2
##   Origin   Total_Outbound
##   <chr>             <dbl>
## 1 Kehancha           4275
## 2 Kilgoris           3190
## 3 Olopi              4165
  1. Total Arrivals (Destination Totals)
destination_summary <- od %>%
  group_by(Destination) %>%
  summarise(
    Total_Inbound = sum(Volume)
  )

destination_summary
## # A tibble: 3 × 2
##   Destination Total_Inbound
##   <chr>               <dbl>
## 1 Kehancha             4855
## 2 Kilgoris             3035
## 3 Olopi                3740
  1. Overall Traffic Summary
od_summary <- od %>%
  summarise(
    Total_Trips = sum(Volume),
    Mean_Flow = mean(Volume),
    Maximum_Flow = max(Volume),
    Minimum_Flow = min(Volume)
  )

od_summary
## # A tibble: 1 × 4
##   Total_Trips Mean_Flow Maximum_Flow Minimum_Flow
##         <dbl>     <dbl>        <dbl>        <dbl>
## 1       11630     1938.         2845         1180
  1. Net Flow Balance
balance <- full_join(
  origin_summary,
  destination_summary,
  by = c("Origin" = "Destination")
) %>%
  mutate(
    Net_Flow = Total_Outbound - Total_Inbound
  )

balance
## # A tibble: 3 × 4
##   Origin   Total_Outbound Total_Inbound Net_Flow
##   <chr>             <dbl>         <dbl>    <dbl>
## 1 Kehancha           4275          4855     -580
## 2 Kilgoris           3190          3035      155
## 3 Olopi              4165          3740      425

Positive values indicate a net producer of trips, while negative values indicate a net attractor.

  1. Opposite-Direction Comparison
pairs <- od %>%
  mutate(
    Pair = map2_chr(
      Origin,
      Destination,
      ~paste(sort(c(.x, .y)), collapse = " - ")
    )
  ) %>%
  group_by(Pair) %>%
  summarise(
    Direction1 = first(Volume),
    Direction2 = last(Volume),
    Difference = abs(Direction1 - Direction2),
    Ratio = round(min(Direction1, Direction2) /
                    max(Direction1, Direction2), 2),
    .groups = "drop"
  )

pairs
## # A tibble: 3 × 5
##   Pair                Direction1 Direction2 Difference Ratio
##   <chr>                    <dbl>      <dbl>      <dbl> <dbl>
## 1 Kehancha - Kilgoris       2010       1715        295  0.85
## 2 Kehancha - Olopi          2845       2560        285  0.9 
## 3 Kilgoris - Olopi          1320       1180        140  0.89

A ratio close to 1.00 indicates well-balanced traffic between the two directions

  1. Heat Map of the O-D Matrix
ggplot(od,
       aes(Destination,
           Origin,
           fill = Volume)) +
  geom_tile(color = "white") +
  geom_text(aes(label = Volume),
            color = "black",
            size = 5) +
  scale_fill_gradient(low = "white",
                      high = "steelblue") +
  labs(title = "Origin-Destination Matrix") +
  theme_minimal()

  1. Export Results to Excel
library(openxlsx)

write.xlsx(
  list(
    "OD Matrix" = od_matrix,
    "Origin Totals" = origin_summary,
    "Destination Totals" = destination_summary,
    "Overall Summary" = od_summary,
    "Balance" = balance,
    "Opposite Directions" = pairs
  ),
  "OD_Analysis.xlsx",
  overwrite = TRUE
)