New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
v0.2.3: Spectrum readers, spectroelectrochemistry refactor, CyclicVoltammogram.plot_cycles #73
Conversation
Hi @KennethNielsen and @Caiwu-L , This is a bit bigger PR which will lead to ixdat 0.2.3 release. I think it's important to get it merged quickly to avoid a long-lived feature branch. I think it's important to get both your approval or critique on the basic data structures represented here, summarized below. In addition, I could use from @Caiwu-L a check that the "qexafs" reader makes sense (things such as using "QexafsFFI0" for y when called with I've been thorough with NEXT_CHANGES.rst, so please read that for an overview of what changed from a user's perspective. Here I'll focus on what has changed in the guts and the implications it might have. The main focus in this (updated) PR is spectrometric data. In ixdat, a spectrum is a 1-D data series (y) that lives in a space defined by one other data series (x), together with associated metadata (including a tstamp). The series x should not be resolved in time (if it is, ixdat would call this a measurement with a single value series). This structure is interfaced by the
Both With this PR, the basic structure for combining a
The relevant tables from ixdat's are drawn here with dbdiagram. A couple things might not be immediately intuitive:
This is reflected, approximately, in the class attributes To indicate how this structure lends itself to be built further upon, consider the following:
A few coding challenges faced here, which you might have some ideas how to resolve:
Otherwise, it was all pretty nice to code. In addition to the heavy spectrum stuff, there are also a few treats in here for present EC and EC-MS users (main one being `CyclicVoltammogram.plot_cycles``) and XPS and XRD readers for me which still should be improved eventually as per the opening comment of this PR, but I now think it's out of scope for this PR. I hope we can get this merged quick! Thanks :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, so I finished. A couple general things. I couldn't quite figure out the expected level of the read, since I couldn't figure out whether someone else would read it/has read it thoroughly. I read it like I normally would, but didn't check the details of whether all the semantics make sense with regards to the objects used i.e. read through of the semantics mostly dealt with whether the code locally made sense.
That being said. A few general points:
- I remember that you before used keyword-only arguments to great effect. I think that is a magnificent idea. Both because we sometimes change signatures and because it improves readability at call site. So maybe consider sprinkling some of that on. Especially e.g. I think more or less all plot functions should make all KW args KWO args.
- As far as the DB diagram. Besides from some concerns about what is possible (which we will talk about later) I would suggest to make a calculated spelling error and call the table "spectrums", so that we can keep the id_columns naming conventions simple, as per my comments in the other PR. I know that misspelling on purpose will annoy some people, but in this case, it really is practical.
- I think maybe you ran out of steam, but the code seems to be a little light on the doc strings on the user facing methods towards the end.
self._xseries = None | ||
self._spectra = None | ||
|
||
@property |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I confess to being a little confused about the way that fields
and xseries
interact with self._xseries
. It seems like there are different algorithms for calculating what will be stored in the property, depending on whether you as for fields or xseries first.
No wait, it is the same result. Then I'm more curious about why it is being calculated in fields and all and not just either not handled at all or retrieved via the xseries property.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, you once again caught my sloppy not-propery-tested coding. I've fixed up the method and added some comments. But the mistakes I can see had to do with the assert being in the wrong place and a forgotten _
, and don't actually address your specific question. To that:
_xseries
was set when the property fields
was called. All the fields have to thave the same or equivalent xseries. So the property xseries
did indeed work like the typical cache. I've rewritten it though to be explicit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah ok. I think my main trigger was the part where the cache was being written on in two methods. That is now gone, but what I'm still (subjectively) slightly wondering is if it wouldn't be cleaner with:
def fields(self):
"""Make sure Fields are loaded and have the same xseries"""
xseries = self.xseries
for i, f in enumerate(self._fields):
if isinstance(f, PlaceHolderObject):
# load or "unpack" any fields for which only the id's were loaded:
self._fields[i] = f.get_object()
# If all the xseries are the same, every field shoud have an xseries identical to the first one
# retrieved via self.xseries
assert self._fields[i].axes_series[0] == xseries
# Now we've loaded any place-holder fields and checked their xseries are equal.
return self._fields
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That does look cleaner, but the problem is that the self.xseries
property calls self.fields
the first time, so it would be an infinite recursion.
Hi @KennethNielsen , thanks for the review! As usual, you caught a lot of mistakes and omissions and the code improved as a result. I've responded to each of the comments and only left a few unresolved where I couldn't immediately implement the suggestions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great. There is one comment that I unresolved, but with a subjective comment, so you decide whether you think it is better. No-matter what, I consider it ready for merge on my account.
self._xseries = None | ||
self._spectra = None | ||
|
||
@property |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah ok. I think my main trigger was the part where the cache was being written on in two methods. That is now gone, but what I'm still (subjectively) slightly wondering is if it wouldn't be cleaner with:
def fields(self):
"""Make sure Fields are loaded and have the same xseries"""
xseries = self.xseries
for i, f in enumerate(self._fields):
if isinstance(f, PlaceHolderObject):
# load or "unpack" any fields for which only the id's were loaded:
self._fields[i] = f.get_object()
# If all the xseries are the same, every field shoud have an xseries identical to the first one
# retrieved via self.xseries
assert self._fields[i].axes_series[0] == xseries
# Now we've loaded any place-holder fields and checked their xseries are equal.
return self._fields
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mainly just reviewed the qexafs reader. It all make sense to me, haven't find problems. Using QexafsFFI0 as y when calling technique 'XAS' is right for sample measuring under fluorescence mode, and this mode is usually used in operando EC-XAS experiment.
There is also a transition mode which is usually used to measure ex-situ standard sample. It has the same data format with the current data, just need to use lnIt/I0 instead of QexafsFFI0 as y in this case.
And finally, l failed to figure out the 'span problem' we saw today in spectrum_plotter :)
v0.2.3: Spectrum readers, spectroelectrochemistry refactor, CyclicVoltammogram.plot_cycles
This PR includes a few small updates for an 0.2.3 release. They are described in NEXT_CHANGES.rst. Briefly:
This makes use of pythons
xml
library. I want to improve it after finishing this XML tutorial. For now I just got it to work as quick as possible.CyclicVoltammogram.plot_cycles()
which makes beautiful colored stuff like this figure. Nice selling point of ixdat to use in demos :) ... however it should probably be moved to aPlotter
@Ejler , if you have time to look at src/ixdat/readers/xrdml.py and especially .../avantage.py, that would be great. I know you have more advanced code for reading the avantage files, which would be great to replace this code with in a future PR :)
@KennethNielsen , if you could just quickly check that the code is readable, that would be great. It's probably not worth going in detail on the structure readers, which I know can be improved. But what is important, I think, is the way the
Spectrum
object is initialized.It may be bad style, but I think these small additions are important for demo'ing ixdat, and so I had people install a pre-release with this unreviewed code for the DTU demo yesterday. That's why I'm PR'ing now even though I know the code could use improvements... and docs and tests are neglected to fall yet further behind.