File size: 1,884 Bytes
3ecb175
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
"""Plot-level aggregation of per-tuber morphometrics.

Per (trial, Plot_std): total projected area and tuber count.
Per (trial, Plot_std, class): class-specific projected area, pivoted
wide to area_{B,A1,A2,OV}_in2 with NaN replaced by 0 for absent classes.
"""

from __future__ import annotations

import pandas as pd

CLASS_AREA_COLUMNS: tuple[str, ...] = (
    "area_B_in2",
    "area_A1_in2",
    "area_A2_in2",
    "area_OV_in2",
)


def aggregate_total_area(df: pd.DataFrame) -> pd.DataFrame:
    """One row per (trial, Plot_std) with total_area_in2 and n_tubers."""
    return (
        df.groupby(["trial", "Plot_std"], as_index=False)
        .agg(
            total_area_in2=("area_poly_in2", "sum"),
            n_tubers=("detection_id", "count"),
        )
    )


def aggregate_area_by_class_wide(df: pd.DataFrame) -> pd.DataFrame:
    """One row per (trial, Plot_std) with one column per USDA class area."""
    long = (
        df.groupby(["trial", "Plot_std", "class"], as_index=False, observed=True)
        .agg(class_area_in2=("area_poly_in2", "sum"))
    )
    wide = (
        long.pivot(index=["trial", "Plot_std"], columns="class", values="class_area_in2")
        .fillna(0)
        .reset_index()
        .rename(
            columns={
                "B": "area_B_in2",
                "A1": "area_A1_in2",
                "A2": "area_A2_in2",
                "OV": "area_OV_in2",
            }
        )
    )
    wide.columns.name = None
    for col in CLASS_AREA_COLUMNS:
        if col not in wide.columns:
            wide[col] = 0.0
    return wide


def aggregate_by_plot(df: pd.DataFrame) -> pd.DataFrame:
    """Combine total-area and class-area-wide aggregations on (trial, Plot_std)."""
    df_total = aggregate_total_area(df)
    df_wide = aggregate_area_by_class_wide(df)
    return df_total.merge(df_wide, on=["trial", "Plot_std"], how="left")