def test_downsampling_consistency(): d = np.arange(1, 24) s = channel.Slice(channel.Continuous(d, with_offset(0), 10)) # Multiple of 5 should downsample to the same irrespective of the method # Source frequency was 1e9 / 10 Hz. So we go to .2e8 Hz. s1 = s.downsampled_to(.2e8) s2 = s.downsampled_by(5) np.testing.assert_allclose(s1.data, s2.data) np.testing.assert_equal(s1.timestamps, s2.timestamps) d = np.arange(1, 24) s = channel.Slice(channel.Continuous(d, with_offset(5), 10)) # Multiple of 5 should downsample to the same irrespective of the method # Source frequency was 1e9 / 10 Hz. So we go to .2e8 Hz. s1 = s.downsampled_to(.2e8) s2 = s.downsampled_by(5) np.testing.assert_allclose(s1.data, s2.data) np.testing.assert_equal(s1.timestamps, s2.timestamps) # Multiple of 5 should downsample to the same irrespective of the method # Source frequency was 1e9 / 7 Hz. d = np.arange(1, 24) s = channel.Slice(channel.Continuous(d, with_offset(0), 7)) s1 = s.downsampled_to(int(1e9 / 7 / 5)) s2 = s.downsampled_by(5) np.testing.assert_allclose(s1.data, s2.data) np.testing.assert_equal(s1.timestamps, s2.timestamps)
def test_calibration_continuous_channels(): time_field = 'Stop time (ns)' mock_calibration = ForceCalibration(time_field=time_field, items=[ {'Calibration Data': 50, time_field: 50}, {'Calibration Data': 20, time_field: 20}, {'Calibration Data': 30, time_field: 30}, {'Calibration Data': 40, time_field: 40}, {'Calibration Data': 80, time_field: 80}, {'Calibration Data': 90, time_field: 90}, {'Calibration Data': 120, time_field: 120}, ]) # Channel should have calibration points 40, 50 since this is the only area that has force data. cc = channel.Slice(channel.Continuous([14, 15, 16, 17], 40, 10), calibration=mock_calibration) assert len(cc.calibration) == 2 assert cc.calibration[0]["Calibration Data"] == 40 assert cc.calibration[1]["Calibration Data"] == 50 # Channel should have calibration points 40, 50, 80, 90, 120 # and time points 40, 50, ... 120 cc = channel.Slice(channel.Continuous(np.arange(14, 23, 1), 40, 10), calibration=mock_calibration) assert len(cc.calibration) == 5 calibration = cc[50:80].calibration assert len(calibration) == 1 assert calibration[0]["Calibration Data"] == 50 calibration = cc[50:90].calibration assert len(calibration) == 2 assert calibration[0]["Calibration Data"] == 50 assert calibration[1]["Calibration Data"] == 80 # Check whether slice nesting works for calibration data # :120 => keeps 40, 50, 80, 90 nested_slice = cc[:120] assert len(nested_slice.calibration) == 4 assert nested_slice.calibration[0]["Calibration Data"] == 40 assert nested_slice.calibration[1]["Calibration Data"] == 50 assert nested_slice.calibration[2]["Calibration Data"] == 80 assert nested_slice.calibration[3]["Calibration Data"] == 90 nested_slice = nested_slice[:90] assert len(nested_slice.calibration) == 3 # 120 and up results in calibration point 120. # This case would be 80 if calibration data would be sliced every time, rather than filtered only when requested. nested_slice = nested_slice[120:] assert len(nested_slice.calibration) == 1 assert nested_slice.calibration[0]["Calibration Data"] == 120
def test_correlation(): cc = channel.Slice(channel.Continuous(np.arange(10, 80, 2), 10, 2)) # Test image stack without dead time fake_tiff = TiffStack( MockTiff([["10", "20"], ["20", "30"], ["30", "40"], ["40", "50"], ["50", "60"], ["60", "70"]])) stack = CorrelatedStack.from_data(fake_tiff) assert (np.allclose( np.hstack([cc[x.start:x.stop].data for x in stack[2:4]]), np.arange(30, 50, 2))) # Test image stack with dead time fake_tiff = TiffStack( MockTiff([["10", "18"], ["20", "28"], ["30", "38"], ["40", "48"], ["50", "58"], ["60", "68"]])) stack = CorrelatedStack.from_data(fake_tiff) assert (np.allclose( np.hstack([cc[x.start:x.stop].data for x in stack[2:4]]), np.hstack([np.arange(30, 38, 2), np.arange(40, 48, 2)]))) # Unit test which tests whether we obtain an appropriately downsampled time series when ask for downsampling of a # slice based on a stack. ch = cc.downsampled_over(stack[0:3].timestamps) assert (np.allclose(ch.data, [ np.mean(np.arange(10, 18, 2)), np.mean(np.arange(20, 28, 2)), np.mean(np.arange(30, 38, 2)) ])) assert (np.allclose(ch.timestamps, [(10 + 18) / 2, (20 + 28) / 2, (30 + 38) / 2])) ch = cc.downsampled_over(stack[1:4].timestamps) assert (np.allclose(ch.data, [ np.mean(np.arange(20, 28, 2)), np.mean(np.arange(30, 38, 2)), np.mean(np.arange(40, 48, 2)) ])) assert (np.allclose(ch.timestamps, [(20 + 28) / 2, (30 + 38) / 2, (40 + 48) / 2])) with pytest.raises(TypeError): cc.downsampled_over(stack[1:4]) with pytest.raises(ValueError): cc.downsampled_over(stack[1:4].timestamps, where='up') with pytest.raises(AssertionError): cc["0ns":"20ns"].downsampled_over(stack[3:4].timestamps) with pytest.raises(AssertionError): cc["40ns":"70ns"].downsampled_over(stack[0:1].timestamps) assert (stack[0].raw.start == 10) assert (stack[1].raw.start == 20) assert (stack[1:3][0].raw.start == 20) assert (stack[1:3].raw[0].start == 20) assert (stack[1:3].raw[1].start == 30)
def test_downsampling(): s = channel.Slice(channel.Continuous([14, 15, 16, 17], start=with_offset(0), dt=10)) assert s.sample_rate == 1e8 s2 = s.downsampled_by(2) np.testing.assert_allclose(s2.data, 14.5, 16.5) np.testing.assert_equal(s2.timestamps, with_offset([5, 25])) assert s2.sample_rate == 0.5e8 s4 = s.downsampled_by(4) np.testing.assert_allclose(s4.data, 15.5) np.testing.assert_equal(s4.timestamps, with_offset([15])) assert s4.sample_rate == 0.25e8 s3 = s.downsampled_by(3) np.testing.assert_allclose(s3.data, 15) np.testing.assert_equal(s3.timestamps, with_offset([10])) assert s3.sample_rate == 33333333 s22 = s2.downsampled_by(2) np.testing.assert_allclose(s22.data, 15.5) np.testing.assert_equal(s22.timestamps, with_offset([15])) assert s22.sample_rate == 0.25e8 with pytest.raises(ValueError): s.downsampled_by(-1) with pytest.raises(TypeError): s.downsampled_by(1.5)
def test_consistency_downsampled_to(): d = np.arange(1, 41) s = channel.Slice(channel.Continuous(d, with_offset(50), 10)) one_step = s.downsampled_to(.1e8) two_step = s.downsampled_to(.2e8).downsampled_to(.1e8) np.testing.assert_allclose(one_step.data, two_step.data) np.testing.assert_allclose(one_step.timestamps, two_step.timestamps)
def test_downsampling_over_subset(): d = np.arange(1, 24) s = channel.Slice(channel.Continuous(d, with_offset(0), 10)) sd = s.downsampled_over([*with_offset([(20, 40), (40, 60), (60, 80)])]) # Data starts at 1, timestamps start at 0. 20-40 corresponds to data [3, 4], 40-60 to [5,6] etc. np.testing.assert_allclose(sd.data, [(3 + 4) / 2, (5 + 6) / 2, (7 + 8) / 2]) np.testing.assert_equal( sd.timestamps, with_offset([(20 + 30) // 2, (40 + 50) // 2, (60 + 70) // 2]) )
def test_start_stop(): s = channel.Slice(channel.TimeSeries([14, 15, 16, 17], [4, 6, 8, 10])) np.testing.assert_allclose(s.start, 4) np.testing.assert_allclose(s.stop, 10 + 1) s = channel.Slice(channel.Continuous([14, 15, 16, 17], 4, 2)) np.testing.assert_allclose(s.start, 4) np.testing.assert_allclose(s.stop, 12) s = channel.Slice(channel.TimeTags([14, 15, 16, 17])) np.testing.assert_allclose(s.start, 14) np.testing.assert_allclose(s.stop, 17 + 1) s = channel.Slice(channel.TimeTags([14, 15, 16, 17], 4, 30)) np.testing.assert_allclose(s.start, 4) np.testing.assert_allclose(s.stop, 30)
def test_plot_correlated_smaller_channel(): from matplotlib.backend_bases import MouseEvent # Regression test for a bug where the start index was added twice. In the regression, this lead to an out of range # error. fake_tiff = TiffStack( MockTiffFile( data=[ np.zeros((3, 3)), np.ones((3, 3)), np.ones((3, 3)) * 2, np.ones((3, 3)) * 3, np.ones((3, 3)) * 4, np.ones((3, 3)) * 5, ], times=make_frame_times(7, step=10), ), align_requested=False, ) # Add test for when there's only a subset in terms of channel data cc = channel.Slice(channel.Continuous(np.arange(10, 80, 2), 30, 2), { "y": "mock", "title": "mock" }) with pytest.warns(UserWarning): CorrelatedStack.from_dataset(fake_tiff).plot_correlated(cc) def mock_click(fig, data_position): pos = fig.axes[0].transData.transform(data_position) fig.canvas.callbacks.process( "button_press_event", MouseEvent("button_press_event", fig.canvas, pos[0], pos[1], 1)) images = [ obj for obj in mpl.pyplot.gca().get_children() if isinstance(obj, mpl.image.AxesImage) ] assert len(images) == 1 return images[0].get_array() np.testing.assert_allclose(mock_click(mpl.pyplot.gcf(), np.array([0, 40])), np.ones((3, 3)) * 2) np.testing.assert_allclose( mock_click(mpl.pyplot.gcf(), np.array([10.1e-9, 40])), np.ones((3, 3)) * 3)
def test_slice_properties(): size = 5 s = channel.Slice(channel.TimeSeries(np.random.rand(size), np.random.rand(size))) assert len(s) == size assert s.sample_rate is None s = channel.Slice(channel.Continuous(np.random.rand(size), start=0, dt=1)) assert len(s) == size assert s.sample_rate == 1e9 size = 10 s = channel.Slice(channel.TimeTags(np.arange(0, size, dtype=np.int64))) assert len(s) == size assert s.sample_rate is None s = channel.empty_slice assert len(s) == 0 assert s.sample_rate is None
def test_downsampling_like(): d = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9] s = channel.Slice(channel.Continuous(d, with_offset(0), 2)) t_downsampled = with_offset([0, 4, 8, 12, 16, 34, 40, 46, 50, 54]) y_downsampled = np.array([0, 1, 2, 3, 4, 6, 7, 8, 9, 10]) reference = channel.Slice(channel.TimeSeries(y_downsampled, t_downsampled)) ds, ref_out = s.downsampled_like(reference) np.testing.assert_equal(ds.timestamps, ref_out.timestamps) np.testing.assert_equal(t_downsampled[1:-1], ds.timestamps) np.testing.assert_allclose(y_downsampled[1:-1], ds.data) with pytest.raises(NotImplementedError): reference.downsampled_like(reference) with pytest.raises(AssertionError): s.downsampled_like(s)
def test_channel_plot(): def testLine(x, y): data = [obj for obj in mpl.pyplot.gca().get_children() if isinstance(obj, mpl.lines.Line2D)] assert len(data) == 1 line = data[0].get_data() np.testing.assert_allclose(line[0], x) np.testing.assert_allclose(line[1], y) d = np.arange(1, 24) s = channel.Slice(channel.Continuous(d, int(5e9), int(10e9))) s.plot() testLine(np.arange(0, 230, 10), d) mpl.pyplot.gca().clear() s.plot(start=0) testLine(np.arange(5, 230, 10), d) mpl.pyplot.gca().clear() s.plot(start=100e9) testLine(np.arange(0, 230, 10) - 100 + 5, d)
def test_continuous_idexing(): s = channel.Slice(channel.Continuous([14, 15, 16, 17], 4, 1)) np.testing.assert_equal(s[0:5].data, [14]) np.testing.assert_equal(s[0:5].timestamps, [4]) np.testing.assert_equal(s[4:5].data, [14]) np.testing.assert_equal(s[4:5].timestamps, [4]) np.testing.assert_equal(s[4:6].data, [14, 15]) np.testing.assert_equal(s[4:6].timestamps, [4, 5]) np.testing.assert_equal(s[4:10].data, [14, 15, 16, 17]) np.testing.assert_equal(s[4:10].timestamps, [4, 5, 6, 7]) s = channel.Slice(channel.Continuous([14, 15, 16, 17], 4, 2)) np.testing.assert_equal(s[0:5].data, [14]) np.testing.assert_equal(s[0:5].timestamps, [4]) np.testing.assert_equal(s[4:5].data, [14]) np.testing.assert_equal(s[4:5].timestamps, [4]) np.testing.assert_equal(s[4:8].data, [14, 15]) np.testing.assert_equal(s[4:8].timestamps, [4, 6]) np.testing.assert_equal(s[4:14].data, [14, 15, 16, 17]) np.testing.assert_equal(s[4:14].timestamps, [4, 6, 8, 10]) with pytest.raises(IndexError) as exc: assert s[1] assert str(exc.value) == "Scalar indexing is not supported, only slicing" with pytest.raises(IndexError) as exc: assert s[1:2:3] assert str(exc.value) == "Slice steps are not supported" s = channel.Slice(channel.TimeSeries([], [])) assert len(s[1:2].data) == 0 assert len(s[1:2].timestamps) == 0 # Regression test for slicing within timestep s = channel.Slice(channel.Continuous([14, 15, 16, 17], 4, 2)) assert s[5:15].timestamps[0] == 6 s = channel.Slice(channel.Continuous([14, 15, 16, 17], -4, 2)) assert s[-3:].timestamps[0] == -2 s = channel.Slice(channel.Continuous([14, 15, 16, 17], 4, 3)) assert s[3:15].timestamps[0] == 4 s = channel.Slice(channel.Continuous([14, 15, 16, 17], 4, 3)) assert s[4:15].timestamps[0] == 4 assert s[5:15].timestamps[0] == 7 assert s[6:15].timestamps[0] == 7 assert s[7:15].timestamps[0] == 7 assert s[8:15].timestamps[0] == 10 assert s[6:14].timestamps[-1] == 13 assert s[6:13].timestamps[-1] == 10
def test_plot_correlated(): cc = channel.Slice(channel.Continuous(np.arange(10, 80, 2), 10, 2), { "y": "mock", "title": "mock" }) # Regression test for a bug where the start index was added twice. In the regression, this lead to an out of range # error. fake_tiff = TiffStack( MockTiffFile( data=[ np.zeros((3, 3)), np.ones((3, 3)), np.ones((3, 3)) * 2, np.ones((3, 3)) * 3, np.ones((3, 3)) * 4, np.ones((3, 3)) * 5, ], times=make_frame_times(7, step=10), ), align_requested=False, ) CorrelatedStack.from_dataset(fake_tiff)[3:5].plot_correlated(cc) imgs = [ obj for obj in mpl.pyplot.gca().get_children() if isinstance(obj, mpl.image.AxesImage) ] assert len(imgs) == 1 np.testing.assert_allclose(imgs[0].get_array(), np.ones((3, 3)) * 3) CorrelatedStack.from_dataset(fake_tiff)[3:5].plot_correlated(cc, frame=1) imgs = [ obj for obj in mpl.pyplot.gca().get_children() if isinstance(obj, mpl.image.AxesImage) ] assert len(imgs) == 1 np.testing.assert_allclose(imgs[0].get_array(), np.ones((3, 3)) * 4)
def test_continuous_downsampling_to(): d = np.arange(1, 24) s = channel.Slice(channel.Continuous(d, with_offset(0), 500)) # 2 MHz # to 1000 ns step s2a = s.downsampled_to(1e6, where='left') np.testing.assert_equal(s2a.timestamps, with_offset(np.arange(0, 11000, 1000))) np.testing.assert_allclose( s2a.data, [1.5, 3.5, 5.5, 7.5, 9.5, 11.5, 13.5, 15.5, 17.5, 19.5, 21.5] ) s2b = s.downsampled_to(1e6, where="center") np.testing.assert_equal( s2b.timestamps, with_offset((np.arange(0, 10500, 1000) + np.arange(500, 10501, 1000)) // 2) ) np.testing.assert_allclose( s2a.data, [1.5, 3.5, 5.5, 7.5, 9.5, 11.5, 13.5, 15.5, 17.5, 19.5, 21.5] ) # upsampling with pytest.raises(ValueError): s.downsampled_to(3e6, where="left") # non-integer ratio with pytest.raises(ValueError): s.downsampled_to(3e5, where="left") # non-integer ratio s3a = s.downsampled_to(3e5, where='left', method="ceil") np.testing.assert_equal(s3a.timestamps, with_offset([0, 3000, 6000])) np.testing.assert_allclose(s3a.data, [3.5, 9.5, 15.5]) s3b = s.downsampled_to(3e5, where='center', method="ceil") np.testing.assert_equal( s3b.timestamps, with_offset([(0 + 2500) // 2, (3000 + 5500) // 2, (6000 + 8500) // 2]) ) np.testing.assert_allclose(s3a.data, [3.5, 9.5, 15.5])
def test_correlation(shape): cc = channel.Slice(channel.Continuous(np.arange(10, 80, 2), 10, 2)) # Test image stack without dead time fake_tiff = TiffStack( MockTiffFile(data=[np.ones(shape)] * 6, times=make_frame_times(6, step=10)), align_requested=False, ) stack = CorrelatedStack.from_dataset(fake_tiff) np.testing.assert_allclose( np.hstack([cc[x.start:x.stop].data for x in stack[2:4]]), np.arange(30, 50, 2)) # Test image stack with dead time fake_tiff = TiffStack(MockTiffFile(data=[np.ones(shape)] * 6, times=make_frame_times(6)), align_requested=False) stack = CorrelatedStack.from_dataset(fake_tiff) np.testing.assert_allclose( np.hstack([cc[x.start:x.stop].data for x in stack[2:4]]), np.hstack([np.arange(30, 38, 2), np.arange(40, 48, 2)]), ) # Unit test which tests whether we obtain an appropriately downsampled time series when ask for downsampling of a # slice based on a stack. ch = cc.downsampled_over(stack[0:3].frame_timestamp_ranges) np.testing.assert_allclose( ch.data, [ np.mean(np.arange(10, 18, 2)), np.mean(np.arange(20, 28, 2)), np.mean(np.arange(30, 38, 2)), ], ) np.testing.assert_allclose(ch.timestamps, [(10 + 16) / 2, (20 + 26) / 2, (30 + 36) / 2]) ch = cc.downsampled_over(stack[1:4].frame_timestamp_ranges) np.testing.assert_allclose( ch.data, [ np.mean(np.arange(20, 28, 2)), np.mean(np.arange(30, 38, 2)), np.mean(np.arange(40, 48, 2)), ], ) np.testing.assert_allclose(ch.timestamps, [(20 + 26) / 2, (30 + 36) / 2, (40 + 46) / 2]) with pytest.raises(TypeError): cc.downsampled_over(stack[1:4]) with pytest.raises(ValueError): cc.downsampled_over(stack[1:4].frame_timestamp_ranges, where="up") with pytest.raises(AssertionError): cc["0ns":"20ns"].downsampled_over(stack[3:4].frame_timestamp_ranges) with pytest.raises(AssertionError): cc["40ns":"70ns"].downsampled_over(stack[0:1].frame_timestamp_ranges) assert stack[0]._get_frame(0).start == 10 assert stack[1]._get_frame(0).start == 20 assert stack[1:3]._get_frame(0).start == 20 assert stack[1:3]._get_frame(0).start == 20 assert stack[1:3]._get_frame(1).start == 30 # Regression test downsampled_over losing precision due to reverting to double rather than int64. cc = channel.Slice( channel.Continuous(np.arange(10, 80, 2), 1588267266006287100, 1000)) ch = cc.downsampled_over([(1588267266006287100, 1588267266006287120)], where="left") assert int(ch.timestamps[0]) == 1588267266006287100
d = np.arange(1, 24) s = channel.Slice(channel.Continuous(d, int(5e9), int(10e9))) s.plot() testLine(np.arange(0, 230, 10), d) mpl.pyplot.gca().clear() s.plot(start=0) testLine(np.arange(5, 230, 10), d) mpl.pyplot.gca().clear() s.plot(start=100e9) testLine(np.arange(0, 230, 10) - 100 + 5, d) def test_regression_lazy_loading(channel_h5_file): ch = channel.Continuous.from_dataset(channel_h5_file["Force HF"]["Force 1x"]) assert type(ch._src._src_data) == h5py.Dataset @pytest.mark.parametrize( "data, new_data", [ (channel.Continuous([1, 2, 3, 4, 5], start=1, dt=1), np.array([5, 6, 7, 8, 9])), (channel.TimeSeries([1, 2, 3, 4, 5], [2, 3, 4, 5, 6]), np.array([5, 6, 7, 8, 9])), ], ) def test_with_data(data, new_data): np.testing.assert_allclose(data._with_data(new_data).data, new_data) old_timestamps = data.timestamps np.testing.assert_allclose(data._with_data(new_data).timestamps, old_timestamps)
def test_seconds_property(): s = channel.Slice(channel.Continuous([14, 15, 16, 17], start=40, dt=1e9)) np.testing.assert_allclose(s.seconds, [0, 1, 2, 3]) s = channel.Slice(channel.TimeSeries([14, 15, 16, 17], [40e9, 41e9, 42e9, 43e9])) np.testing.assert_allclose(s.seconds, [0, 1, 2, 3])