Reader for .ita files and test functions for it#839
Reader for .ita files and test functions for it#839
Conversation
| if ita_coordinates.cart.size == 0: | ||
| return pf.Coordinates() | ||
| if ita_coordinates.cart.shape[-1] != 3: | ||
| raise ValueError( |
There was a problem hiding this comment.
I also thought about implementing a function to test this ValueError, but I was unable to create an .ita file with an invalid number of dimensions (as there are enough exceptions for this case in MATLAB).
If anyone knows how to test this, I would appreciate some help :)
There was a problem hiding this comment.
seems to be difficult, in this case you can skip the test, but we should keep the error handling here
sikersten
left a comment
There was a problem hiding this comment.
Thanks finalizing this!
- Please check the reason for the failing tests, I think this may have to do with obsolete test filtes.
- I would suggest removing all stars * before .ita in the docstrings, since these are strangly rendered: https://pyfar--839.org.readthedocs.build/en/839/modules/pyfar.io.html#pyfar.io.read_ita
pyfar/io/io.py
Outdated
|
|
||
|
|
||
| def read_ita(filename): | ||
| """Read a *.ita file from the ITA-toolbox. |
There was a problem hiding this comment.
It may be nice to link to the toolbox here (adding it as reference below): https://www.ita-toolbox.org/
pyfar/io/io.py
Outdated
| Returns | ||
| ------- | ||
| data : pyfar.Signal, pyfar.TimeData, pyfar.FrequencyData | ||
| The data contained in the *.ita file. |
There was a problem hiding this comment.
| The data contained in the *.ita file. | |
| The audio data contained in the *.ita file. |
pyfar/io/io.py
Outdated
|
|
||
|
|
||
| def read_ita(filename): | ||
| """Read a *.ita file from the ITA-toolbox. |
There was a problem hiding this comment.
| """Read a *.ita file from the ITA-toolbox. | |
| """Read an *.ita file created using the ITA-toolbox. |
pyfar/io/io.py
Outdated
| return data, object_coordinates, channel_coordinates, metadata | ||
|
|
||
|
|
||
| def _to_coordinates(ita_coordinates): |
There was a problem hiding this comment.
| def _to_coordinates(ita_coordinates): | |
| def _ita_to_pyfar_coordinates(ita_coordinates): |
|
|
||
| # checks if *.ita object is itaAudio or itaResult | ||
| if (hasattr(matlab_data, 'signalType')): # itaAudio | ||
| fft_norm = 'none' if matlab_data.signalType == 'energy' else 'rms' |
There was a problem hiding this comment.
Maybe add a comment here, that this results from the ITA-toolbox logic?
pyfar/io/io.py
Outdated
| weights=weights) | ||
|
|
||
| # import as spherical coordinates: r, theta, phi | ||
| if hasattr(ita_coordinates, 'sph'): |
There was a problem hiding this comment.
Is it possible that ita_coordinates has both attributes "cart" and "sph"? If not, this would need to be an elif connected to line 1035
There was a problem hiding this comment.
I am not an expert on the ITA-Toolbox, but I think that ita_coordinates can have both attributes at the same time.
There was a problem hiding this comment.
@mberz can you clarify? The files I saw, that contained both had identical data stored in different coordinate conventions, I think.
There was a problem hiding this comment.
I can either be 'cart' or 'sph', similar to what we has before in our coordinates class.
pyfar/io/io.py
Outdated
| ita_coordinates.sph[..., 0], | ||
| weights=weights) | ||
| else: | ||
| return pf.Coordinates() |
There was a problem hiding this comment.
I would suggest returning None here, since this object won't be of any use
tests/test_io.py
Outdated
| """Tests correct exception raise for example *.ita files.""" | ||
| filepath = os.path.join('tests', 'test_io_data', filename) | ||
| if (filename == 'freq_itaResult.ita' and data_type == 'signal'): | ||
| message = "The itaResult object can't contain a signal." |
There was a problem hiding this comment.
This message is not defined in the function?
tests/test_io_read_ita_ahe.py
Outdated
There was a problem hiding this comment.
I think this file should not be here
There was a problem hiding this comment.
Please move the added content to test_io_read_ita.py
There was a problem hiding this comment.
Thank you for your review!
I just noticed that this file tests/test_io.py contains an outdated test function test_read_ita(filename, data_type) that we don't need. Therefore, I would simply remove this function from the file.
Thanks! (I've already tested it, it works, and it looks like cursive.) |
f-brinkmann
left a comment
There was a problem hiding this comment.
Thanks! I found data that is returned twice. Apart from that its mostly small suggestions.
pyfar/io/io.py
Outdated
| # import as spherical coordinates: r, theta, phi | ||
| if hasattr(ita_coordinates, 'sph'): | ||
| if ita_coordinates.cart.size == 0: | ||
| return pf.Coordinates() |
There was a problem hiding this comment.
See comment above from sikersten. I would suggest to return None here, too.
pyfar/io/io.py
Outdated
| data_in, matlab_data.abscissa, | ||
| comment=comment) | ||
|
|
||
| metadata = _matlab_to_dict(matlab_data) |
There was a problem hiding this comment.
This return redundant information, e.g., matlab_data.sampling_rate and matlab_data.data that are already returned in the Signal. I would suggest to remove all data that is already returned elsewehre.
There was a problem hiding this comment.
Thank you for your review!
I'm not quite sure what exactly I should do.
Are you suggesting including less redundant data in the metadata? For example, removing the matlab_data.domain or matlab_data.sampling_rate from the metadata?
Wouldn't that be too confusing? Because the variable data stores an instance of pf.Signal/pf.TimeData/pf.FrequencyData, and in the variable metadata we only have the raw data for this instance, not the instance itself.
There was a problem hiding this comment.
Good point Fabian, since there is not straight forward solution for this, I would still leave it as it is.
There was a problem hiding this comment.
Would it make sense to exclude at least the matlab_data.data from metadata? This bascially doubles the RAM requirements and might be annyoing if reading large files.
pyfar/io/io.py
Outdated
| object_coordinates : pyfar.Coordinates | ||
| The object coordinates of the `*.ita` file. | ||
| channel_coordinates : pyfar.Coordinates | ||
| The channel coordinates of the `*.ita` file. |
There was a problem hiding this comment.
In case we follow sikerstens suggestion below, this should become
| object_coordinates : pyfar.Coordinates | |
| The object coordinates of the `*.ita` file. | |
| channel_coordinates : pyfar.Coordinates | |
| The channel coordinates of the `*.ita` file. | |
| object_coordinates : pyfar.Coordinates, None | |
| The object coordinates of the `*.ita` file if they exist, ``None`` otherwise. | |
| channel_coordinates : pyfar.Coordinates, None | |
| The channel coordinates of the `*.ita` file if they exist, ``None`` otherwise. |
There was a problem hiding this comment.
Maybe add a note on the difference of object and channel coordinates? Do we need both? Just asking because I'm not familiar with this.
There was a problem hiding this comment.
The ITA documentation in MATLAB contains some brief descriptions:
channelCoordinates: coordinates for each channel (must be itaCoordinates)
objectCoordinates: the coordinates of the measured object
Would that be enough for us? I am also not very familiar with the purpose and functionality of channelCoordinates and objectCoordinates, so I would appreciate the opinion of someone who worked with this :)
There was a problem hiding this comment.
Same concept as in sofa with listener and receiver.
One object can have multiple channels with relative positioning to the object.
pyfar/io/io.py
Outdated
| weights=weights) | ||
|
|
||
| # import as spherical coordinates: r, theta, phi | ||
| if hasattr(ita_coordinates, 'sph'): |
There was a problem hiding this comment.
@mberz can you clarify? The files I saw, that contained both had identical data stored in different coordinate conventions, I think.
pyfar/io/io.py
Outdated
| mat_dict = {} | ||
| for fieldname in mat_obj._fieldnames: | ||
| elem = mat_obj.__dict__[fieldname] | ||
| if isinstance(elem, spio.matlab.mio5_params.mat_struct): |
There was a problem hiding this comment.
I think this line causes the deprecation errors and should be changed accordingly:
DeprecationWarning: Please import
mat_structfrom thescipy.io.matlabnamespace; thescipy.io.matlab.mio5_paramsnamespace is deprecated and will be removed in SciPy 2.0.0.
pyfar/io/io.py
Outdated
| """ | ||
| Read an `*.ita` file created using the ITA-toolbox. | ||
|
|
||
| ITA-Tollbox is a MATLAB Toolbox for Acoustics [#]_. |
There was a problem hiding this comment.
| ITA-Tollbox is a MATLAB Toolbox for Acoustics [#]_. | |
| ITA-Toolbox is a MATLAB Toolbox for Acoustics [#]_. |
|
|
||
|
|
||
| def _ita_to_pyfar_coordinates(ita_coordinates): | ||
| if hasattr(ita_coordinates, 'weights'): |
There was a problem hiding this comment.
maybe add a short docstring here.
pyfar/io/io.py
Outdated
| weights=weights) | ||
|
|
||
| # import as spherical coordinates: r, theta, phi | ||
| if hasattr(ita_coordinates, 'sph'): |
There was a problem hiding this comment.
I can either be 'cart' or 'sph', similar to what we has before in our coordinates class.
pyfar/io/io.py
Outdated
|
|
||
|
|
||
| def _matlab_to_dict(mat_obj): | ||
| """Recursively transform MATLAB struct objects to nested dictionaries.""" |
There was a problem hiding this comment.
maybe also add documation about input and output of the method
There was a problem hiding this comment.
maybe move to tests/test_io_data/read_ita/* in this way it would be cleaner and easier to distinguish where it bolongs.
tests/test_io_read_ita.py
Outdated
| assert np.array_equal(metadata_channel_coords_reshaped, | ||
| channel_coords.cartesian) and \ | ||
| np.array_equal(channel_coords.cartesian, channelCoordinates) |
There was a problem hiding this comment.
| assert np.array_equal(metadata_channel_coords_reshaped, | |
| channel_coords.cartesian) and \ | |
| np.array_equal(channel_coords.cartesian, channelCoordinates) | |
| assert np.array_equal(metadata_channel_coords_reshaped, | |
| channel_coords.cartesian) | |
| assert np.array_equal(channel_coords.cartesian, channelCoordinates) |
| import os | ||
| import pytest | ||
| import numpy as np | ||
|
|
There was a problem hiding this comment.
is there any test about the metadata which is not part of other returned data?
There was a problem hiding this comment.
Thank you for the review!
Regarding your question: No, not really.
Which data from the metadata should I also test?
In the metadata dictionary there are also keys such as "classname", "classversion", "comment", "history", "fileName", "dateCreated", "dateModified", "dateSaved", "userData", "plotAxesProperties", "plotLineProperties", "userName", "channelNames", "channelUnits", "channelSensors", "channelUserData", "dataTypeOutput", but they are mostly empty in most files we are testing.
Which issue(s) will be closed by this pull request?
Replaces #720
Closes #206
Here is a draft implementation of test functions for the .ita Reader. If you have any suggestions or comments regarding the test functions or the
read_itafunction itself, please do not hesitate to write them.