devjas1 commited on
Commit
88e8763
·
1 Parent(s): a64b261

(TESTS)[FTIR]: Add FTIR preprocessing unit tests

Browse files

- Introduced new test file for FTIR-specific preprocessing routines.
- Validates baseline correction, smoothing, normalization, and modality-specific handling of FTIR spectrum data.
- Includes multiple test cases for input formats and edge conditions to ensure correct preprocessing output.
- Ensures robustness of preprocessing for FTIR files in TXT, CSV, and JSON formats.

Files changed (1) hide show
  1. tests/test_ftir_preprocessing.py +179 -0
tests/test_ftir_preprocessing.py ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Tests for FTIR preprocessing functionality."""
2
+
3
+ import pytest
4
+ import numpy as np
5
+ from utils.preprocessing import (
6
+ preprocess_spectrum,
7
+ validate_spectrum_range,
8
+ get_modality_info,
9
+ MODALITY_RANGES,
10
+ MODALITY_PARAMS,
11
+ )
12
+
13
+
14
+ def test_modality_ranges():
15
+ """Test that modality ranges are correctly defined."""
16
+ assert "raman" in MODALITY_RANGES
17
+ assert "ftir" in MODALITY_RANGES
18
+
19
+ raman_range = MODALITY_RANGES["raman"]
20
+ ftir_range = MODALITY_RANGES["ftir"]
21
+
22
+ assert raman_range[0] < raman_range[1] # Valid range
23
+ assert ftir_range[0] < ftir_range[1] # Valid range
24
+ assert ftir_range[0] >= 400 # FTIR starts at 400 cm⁻¹
25
+ assert ftir_range[1] <= 4000 # FTIR ends at 4000 cm⁻¹
26
+
27
+
28
+ def test_validate_spectrum_range():
29
+ """Test spectrum range validation for different modalities."""
30
+ # Test Raman range validation
31
+ raman_x = np.linspace(300, 3500, 100) # Typical Raman range
32
+ assert validate_spectrum_range(raman_x, "raman") == True
33
+
34
+ # Test FTIR range validation
35
+ ftir_x = np.linspace(500, 3800, 100) # Typical FTIR range
36
+ assert validate_spectrum_range(ftir_x, "ftir") == True
37
+
38
+ # Test out-of-range data
39
+ out_of_range_x = np.linspace(50, 150, 100) # Too low for either
40
+ assert validate_spectrum_range(out_of_range_x, "raman") == False
41
+ assert validate_spectrum_range(out_of_range_x, "ftir") == False
42
+
43
+
44
+ def test_ftir_preprocessing():
45
+ """Test FTIR-specific preprocessing parameters."""
46
+ # Generate synthetic FTIR spectrum
47
+ x = np.linspace(400, 4000, 200) # FTIR range
48
+ y = np.sin(x / 500) + 0.1 * np.random.randn(len(x)) + 2.0 # Synthetic absorbance
49
+
50
+ # Test FTIR preprocessing
51
+ x_proc, y_proc = preprocess_spectrum(x, y, modality="ftir", target_len=500)
52
+
53
+ assert x_proc.shape == (500,)
54
+ assert y_proc.shape == (500,)
55
+ assert np.all(np.diff(x_proc) > 0) # Monotonic increasing
56
+ assert np.min(y_proc) >= 0.0 # Normalized to [0, 1]
57
+ assert np.max(y_proc) <= 1.0
58
+
59
+
60
+ def test_raman_preprocessing():
61
+ """Test Raman-specific preprocessing parameters."""
62
+ # Generate synthetic Raman spectrum
63
+ x = np.linspace(200, 3500, 200) # Raman range
64
+ y = np.exp(-(((x - 1500) / 200) ** 2)) + 0.05 * np.random.randn(
65
+ len(x)
66
+ ) # Gaussian peak
67
+
68
+ # Test Raman preprocessing
69
+ x_proc, y_proc = preprocess_spectrum(x, y, modality="raman", target_len=500)
70
+
71
+ assert x_proc.shape == (500,)
72
+ assert y_proc.shape == (500,)
73
+ assert np.all(np.diff(x_proc) > 0) # Monotonic increasing
74
+ assert np.min(y_proc) >= 0.0 # Normalized to [0, 1]
75
+ assert np.max(y_proc) <= 1.0
76
+
77
+
78
+ def test_modality_specific_parameters():
79
+ """Test that different modalities use different default parameters."""
80
+ x = np.linspace(400, 4000, 200)
81
+ y = np.sin(x / 500) + 1.0
82
+
83
+ # Test that FTIR uses different window length than Raman
84
+ ftir_params = MODALITY_PARAMS["ftir"]
85
+ raman_params = MODALITY_PARAMS["raman"]
86
+
87
+ assert ftir_params["smooth_window"] != raman_params["smooth_window"]
88
+
89
+ # Preprocess with both modalities (should use different parameters)
90
+ x_raman, y_raman = preprocess_spectrum(x, y, modality="raman")
91
+ x_ftir, y_ftir = preprocess_spectrum(x, y, modality="ftir")
92
+
93
+ # Results should be slightly different due to different parameters
94
+ assert not np.allclose(y_raman, y_ftir, rtol=1e-10)
95
+
96
+
97
+ def test_get_modality_info():
98
+ """Test modality information retrieval."""
99
+ raman_info = get_modality_info("raman")
100
+ ftir_info = get_modality_info("ftir")
101
+
102
+ assert "range" in raman_info
103
+ assert "params" in raman_info
104
+ assert "range" in ftir_info
105
+ assert "params" in ftir_info
106
+
107
+ # Check that ranges match expected values
108
+ assert raman_info["range"] == MODALITY_RANGES["raman"]
109
+ assert ftir_info["range"] == MODALITY_RANGES["ftir"]
110
+
111
+ # Check that parameters are present
112
+ assert "baseline_degree" in raman_info["params"]
113
+ assert "smooth_window" in ftir_info["params"]
114
+
115
+
116
+ def test_invalid_modality():
117
+ """Test handling of invalid modality."""
118
+ x = np.linspace(1000, 2000, 100)
119
+ y = np.sin(x / 100)
120
+
121
+ with pytest.raises(ValueError, match="Unsupported modality"):
122
+ preprocess_spectrum(x, y, modality="invalid")
123
+
124
+ with pytest.raises(ValueError, match="Unknown modality"):
125
+ validate_spectrum_range(x, "invalid")
126
+
127
+ with pytest.raises(ValueError, match="Unknown modality"):
128
+ get_modality_info("invalid")
129
+
130
+
131
+ def test_modality_parameter_override():
132
+ """Test that modality defaults can be overridden."""
133
+ x = np.linspace(400, 4000, 100)
134
+ y = np.sin(x / 500) + 1.0
135
+
136
+ # Override FTIR default window length
137
+ custom_window = 21 # Different from FTIR default (13)
138
+
139
+ x_proc, y_proc = preprocess_spectrum(
140
+ x, y, modality="ftir", window_length=custom_window
141
+ )
142
+
143
+ assert x_proc.shape[0] > 0
144
+ assert y_proc.shape[0] > 0
145
+
146
+
147
+ def test_range_validation_warning():
148
+ """Test that range validation warnings work correctly."""
149
+ # Create spectrum outside typical FTIR range
150
+ x_bad = np.linspace(100, 300, 50) # Too low for FTIR
151
+ y_bad = np.ones_like(x_bad)
152
+
153
+ # Should still process but with validation disabled
154
+ x_proc, y_proc = preprocess_spectrum(
155
+ x_bad, y_bad, modality="ftir", validate_range=False # Disable validation
156
+ )
157
+
158
+ assert len(x_proc) > 0
159
+ assert len(y_proc) > 0
160
+
161
+
162
+ def test_backwards_compatibility():
163
+ """Test that old preprocessing calls still work (defaults to Raman)."""
164
+ x = np.linspace(1000, 2000, 100)
165
+ y = np.sin(x / 100)
166
+
167
+ # Old style call (should default to Raman)
168
+ x_old, y_old = preprocess_spectrum(x, y)
169
+
170
+ # New style call with explicit Raman
171
+ x_new, y_new = preprocess_spectrum(x, y, modality="raman")
172
+
173
+ # Should be identical
174
+ np.testing.assert_array_equal(x_old, x_new)
175
+ np.testing.assert_array_equal(y_old, y_new)
176
+
177
+
178
+ if __name__ == "__main__":
179
+ pytest.main([__file__])