Repository: cta-observatory/ctapipe (python)

Issue:
DataWriter: Caught unexpected exception: 'float' object has no attribute 'to_value'
**Describe the bug**
Looks like we can not write the muonparameters_skewness during the muon analysis 
Error writing col muonparameters_skewness of container MuonParametersContainer

**To Reproduce**
```
ctapipe-process --overwrite --input=$in_simtel_file --output=$out_r0_dl1_h5_file --config=$config_file
```

$config_file:
```yaml
DataWriter:
  output_path: None
  write_muon_parameters: False
  write_dl1_parameters: False
  write_dl1_images: True
  overwrite: True

MuonProcessor:
  completeness_threshold: 30
  ratio_width: 1.5
  pedestal: 1.1

MuonIntensityFitter:
  hole_radius_m: [["type", "LST_*", 0.308]]
  spe_width: 0.5
  min_lambda_m: 0.0000003
  max_lambda_m: 0.0000006
  oversampling: 3

ImageParameterQuery:
  quality_criteria: [
      ["min_pixels", "dl1_params.morphology.n_pixels > 100"],
      ["min_intensity", "dl1_params.hillas.intensity > 500"]
    ]

RingQuery:
  quality_criteria: [
      ["radius_not_nan", "np.isfinite(ring.radius.value)"],
      ["min_pixels", "np.count_nonzero(mask) > 50"],
      ["ring_containment", "parameters.containment > 0.5"]
    ]

ImageProcessor:
  image_cleaner_type: "TailcutsImageCleaner"
  use_telescope_frame: True
  apply_image_modifier: False

TailcutsImageCleaner:
  picture_threshold_pe : 10
  boundary_threshold_pe : 5
  min_picture_neighbors :  2
  keep_isolated_pixels : False

TimeConstrainedImageCleaner:
  time_limit_core_ns: 4.5
  time_limit_boundary_ns: 1.5

ImageQualityQuery:
  quality_criteria: [["size_greater_0", "image.sum() > 0"]]

CameraCalibrator:
  data_volume_reducer_type: "NullDataVolumeReducer"
  image_extractor_type: "GlobalPeakWindowSum"
  invalid_pixel_handler_type: "NeighborAverage"
  apply_waveform_time_shift: False
  apply_peak_time_shift: True

GlobalPeakWindowSum:
  window_width: 7
  window_shift: 3
  apply_integration_correction: True
  pixel_fraction: 1
```



**Expected behavior**
normal operation with output file together with muon parameters 


Pull request:
Force absences of units for skewness and excess_kurtosis
Explicitly convert (dimensionless) skewness and excess_kurtosis to values

Closes #2730 

Patch:
diff --git a/docs/changes/2732.bugfix.rst b/docs/changes/2732.bugfix.rst
new file mode 100644
index 00000000..d5ded894
--- /dev/null
+++ b/docs/changes/2732.bugfix.rst
@@ -0,0 +1,1 @@
+Fix units (absence of) for skewness and excess kurtosis in muon analysis
diff --git a/src/ctapipe/image/muon/features.py b/src/ctapipe/image/muon/features.py
index 8d547bf5..f18f1c3f 100644
--- a/src/ctapipe/image/muon/features.py
+++ b/src/ctapipe/image/muon/features.py
@@ -324,8 +324,8 @@ def radial_light_distribution(
     if np.sum(image) == 0:
         return (
             np.nan * u.deg,
-            np.nan * u.dimensionless_unscaled,
-            np.nan * u.dimensionless_unscaled,
+            np.nan,
+            np.nan,
         )
 
     pixel_r = np.hypot(pixel_fov_lon - center_fov_lon, pixel_fov_lat - center_fov_lat)
@@ -333,14 +333,10 @@ def radial_light_distribution(
     mean = np.average(pixel_r, weights=image)
     delta_r = pixel_r - mean
     radial_std_dev = np.sqrt(np.average(delta_r**2, weights=image))
-    skewness = (
-        np.average(delta_r**3, weights=image)
-        / radial_std_dev**3
-        * u.dimensionless_unscaled
-    )
+    skewness = (np.average(delta_r**3, weights=image) / radial_std_dev**3).to_value()
     excess_kurtosis = (
         np.average(delta_r**4, weights=image) / radial_std_dev**4 - 3.0
-    ) * u.dimensionless_unscaled
+    ).to_value()
 
     return (
         radial_std_dev,


Test patch:
diff --git a/src/ctapipe/image/muon/tests/test_muon_features.py b/src/ctapipe/image/muon/tests/test_muon_features.py
index 0ed8e104..b9f44ed8 100644
--- a/src/ctapipe/image/muon/tests/test_muon_features.py
+++ b/src/ctapipe/image/muon/tests/test_muon_features.py
@@ -142,6 +142,12 @@ def test_radial_light_distribution():
     )
 
     assert radial_std_dev.unit == u.deg
+    assert isinstance(
+        skewness, (float, np.floating)
+    ), f"Unexpected type: {type(skewness)}"
+    assert isinstance(
+        excess_kurtosis, (float, np.floating)
+    ), f"Unexpected type: {type(excess_kurtosis)}"
     assert np.isclose(radial_std_dev, expected_std_dev, atol=1e-2)
     assert np.isclose(skewness, expected_skewness, atol=1e-2)
     assert np.isclose(excess_kurtosis, expected_excess_kurtosis, atol=1e-2)