-
Notifications
You must be signed in to change notification settings - Fork 97
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
Adding a degree of freedom (dof) flag (--n-independent-echos) to allow users to set the dof used in fstat calculations #1177
base: main
Are you sure you want to change the base?
Conversation
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.
Besides the one ongoing discussion about a warning message, the rest of this looks good.
We should definitely include some tests. I can help with those, if useful.
adding tests. simplifying getfbounds
@handwerkerd The tests/changes you made are in the latest commit. I also made a change to fix the fstat map test you added for echo_dof.
One of the asserts is still failing in fstat map test. Its caused by the following failing. Should this test be strictly greater than 0? I think maybe the f_max may be preventing that. Can you take a look?
|
Since the tests were passing on my local computer & failing here, I decided to push some commits to see if I could just solve it. I think the key problem was we have a maximum F value set to 500. That means, for a test >500, the F is a constant 500 and the difference between DOF==3 vs 5 is 0. Since the goal of the test was to make sure the change in DOF always changed the F stats, I needed to mask out the F==500 tests for both f_t2 and f_s0. |
@tsalo Do you want to take another look or is this good to merge? |
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 have a few small requested changes. The main thing I'd like to see changed is to consistently refer to these numbers as either the number of independent echoes or the degrees of freedom. AFAIK the latter is one less than the former, but the terms are used interchangeably in this PR.
I'm also not clear on how the adaptive mask is used when echo_dof
is defined. It's not used in the F-statistic estimation, but is it still used to calculate T2* maps and optimally combine data across echoes?
if echo_dof is None: | ||
f_thresh, _, _ = getfbounds(len(tes)) | ||
else: | ||
f_thresh, _, _ = getfbounds(echo_dof) |
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.
It would be simpler to just set echo_dof
as len(tes)
if it's not provided, at the top of this function.
However, I am concerned that setting the degrees of freedom based on the number of echoes is already a bug (see #811). It just doesn't account for the fact that the degrees of freedom varies across voxels due to the adaptive mask.
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 agree on this change.
Addressing #811 might be useful, but seems beyond the scope of this PR. If we're clarifying which inputs are supposed to be DOF, this might be easier to address.
# DOF is number of echoes if not otherwise specified | ||
echo_dof = selector.cross_component_metrics_["n_echos"] |
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.
Just to clarify- DOF wouldn't be the number of echoes, right? It would be the number of echoes minus 1.
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.
ug. I'm fairly sure in all calculations that use echo_dof, it's echo_dof - 1
. The math is correct, but the terminology is wrong. This also brings up an issue for @katielamar in that, if echo_dof is the true DOF, should we run calculations with echo_dof
and not echo_dof - 1
?
I think a terminology-consistent solution should be to make echo_dof
the DOF, when we calculate this based on the number of echoes, echo_dof = n_echoes - 1
and we'd then need to update all calculations that use these values to not subtract another -1
.
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.
You comment below of changing echo_dof
to n_independent_sources
is another solution that would not require changing formulas.
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.
Hmm yeah I'm not sure. Im going to check with Tom on this and Ill get back to you.
if echo_dof is None: | ||
fmin, fmid, fmax = getfbounds(n_echos) | ||
else: | ||
fmin, fmid, fmax = getfbounds(echo_dof) |
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.
if echo_dof is None: | |
fmin, fmid, fmax = getfbounds(n_echos) | |
else: | |
fmin, fmid, fmax = getfbounds(echo_dof) | |
echo_dof = echo_dof or n_echos | |
fmin, fmid, fmax = getfbounds(n_echos) |
f05 = stats.f.ppf(q=(1 - 0.05), dfn=1, dfd=(echo_dof - 1)) | ||
f025 = stats.f.ppf(q=(1 - 0.025), dfn=1, dfd=(echo_dof - 1)) | ||
f01 = stats.f.ppf(q=(1 - 0.01), dfn=1, dfd=(echo_dof - 1)) |
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 think it's misleading to call the parameter dof and then subtract 1, unless I'm misunderstanding the math. We should change the function to use the degrees of freedom and pass in n_echos - 1
as needed.
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.
Or just change the echo_dof
variable to n_independent_sources
/n_independent_echos
.
# For EPTI sequences, the way we use adaptive mask thresholding fails | ||
# because sequential echoes have overlapping information. |
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.
# For EPTI sequences, the way we use adaptive mask thresholding fails | |
# because sequential echoes have overlapping information. | |
# For EPTI sequences, the way we use adaptive mask thresholding fails | |
# because sequential echoes have overlapping information and | |
# there is no clear mapping between the independent sources and the echoes. |
If we knew how much each independent source contributed to each echo we could figure this out.
f"{n_3dof_voxels} voxels ({np.round(perc_3dof_voxels, decimals=1)}%) have fewer " | ||
f"than {np.round(threshold_3dof)} " | ||
"good voxels. These voxels will be used in all analyses, " | ||
"but might not include 3 independant echo measurements." |
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.
"but might not include 3 independant echo measurements." | |
"but might not include 3 independent echo measurements." |
f"{n_dof_voxels} voxels ({np.round(perc_dof_voxels, decimals=1)}%) have fewer " | ||
f"than {np.round(threshold_dof)} good voxels. " | ||
f"The degrees of freedom for fits across echoes will remain {echo_dof} even if " | ||
"there might be fewer independant echo measurements." |
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.
"there might be fewer independant echo measurements." | |
"there might be fewer independent echo measurements." |
"good voxels. These voxels will be used in all analyses, " | ||
"but might not include 3 independant echo measurements." | ||
) | ||
# There's a separate warning about DOF if it's possible there's a DOF reduction. |
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.
# There's a separate warning about DOF if it's possible there's a DOF reduction. | |
# There's a separate warning about DOF if it's possible there's a DOF reduction. |
# For EPTI sequences, the way we use adaptive mask thresholding fails | ||
# because sequential echoes have overlapping information. | ||
# Since EPTI has less dropout, it is unclear how often this will cause issues. | ||
# To track this, we are flagging voxels that might mark less independant signal. |
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.
# To track this, we are flagging voxels that might mark less independant signal. | |
# To track this, we are flagging voxels that might mark less independent signal. |
f"than {np.round(threshold_3dof)} " | ||
"good voxels. These voxels will be used in all analyses, " |
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.
f"than {np.round(threshold_3dof)} " | |
"good voxels. These voxels will be used in all analyses, " | |
f"than {np.round(threshold_3dof)} good voxels. " | |
"These voxels will be used in all analyses, " |
Hi everyone,
I've updated the tedana code to address issue #1168. I've made this a draft PR since this is my first time contributing to Tedana and I'm a bit new to submitting pull requests. Let me know if you have any suggestions on how I can improve the documentation/code.
Closes #1168.
Summary of Updates:
--n-independent-echos
to set the degrees of freedom when running tedana workflow from the command lineecho_dof
when calling thetedana_workflow
function