keflavich/cube-line-extractor

SpectralCube use of `.max` on cube with NaNs needs modification

jmangum opened this issue · 7 comments

The following line:

max_map = peak_amplitude = brightest_cube.max(axis=0)

...produces an all-NaN slice warning under some circumstances. I think that this could be fixed with the use of a function like numpy.nanmax. How does one do this with SpectralCube?

SpectralCube always uses nanmax. This is saying that you're trying to do np.nanmax([nan, nan, nan]), which will still return nan. You can silence the warning?

Thanks. Probably should not silence as I don't think that brightest_cube.max(axis=0) should be an all-NaN slice. Looking for a bug here as brightest_cube in this case is a cutoutcube that is not the same as `cube' (change implemented last week).

Hmmmm. Tested simple use case where cube and cutoutcube are the same and still get this warning. Still searching for source of all-NaN slice in cutoutcube.

Now wondering if there is a bug in the max_map calculation. The pertinent section of code (with some debugging print statements thrown-in) is:

    # Create a copy of the cutoutcube with velocity units
    cutoutVcube = cutoutcube.with_spectral_unit(u.km/u.s,
                                                   rest_value=brightest_line_frequency,
                                                   velocity_convention='optical')

    # Use the brightest line to identify the appropriate peak velocities, but ONLY
    # from a slab including +/- width:
    brightest_cube = cutoutVcube.spectral_slab(vz-velocity_half_range,
                                               vz+velocity_half_range)
    print('vz: ',vz)
    print('velocity_half_range: ',velocity_half_range)
    print('brightest_cube spectral axis: ',brightest_cube.spectral_axis)
    print('brightest_cube unmasked values: ',brightest_cube.unmasked_data[0,:,:])

    # compute various moments & statistics along the spcetral dimension
    peak_velocity = brightest_cube.spectral_axis[brightest_cube.argmax(axis=0)]
    print('Peak Velocity: ',peak_velocity)
    print('All NaN Slice Here?')
    max_map = peak_amplitude = brightest_cube.max(axis=0) # This is sometimes as all-NaN slice
    print('max_map: ',max_map)
    print('...maybe not...')
    width_map = brightest_cube.linewidth_sigma() # or vcube.moment2(axis=0)**0.5
    centroid_map = brightest_cube.moment1(axis=0)

This produces the following output:

vz:  236.0 km / s
velocity_half_range:  400.0 km / s
brightest_cube spectral axis:  [ 638.82525781  628.79156544  618.75854325  608.72619118  598.69450915
  588.66349709  578.63315496  568.60348266  558.57448015  548.54614734
  538.51848418  528.49149059  518.46516652  508.43951189  498.41452663
  488.39021068  478.36656398  468.34358644  458.32127802  448.29963863
  438.27866822  428.25836672  418.23873405  408.21977016  398.20147497
  388.18384843  378.16689045  368.15060098  358.13497994  348.12002728
  338.10574292  328.0921268   318.07917885  308.066899    298.05528719
  288.04434335  278.03406741  268.02445931  258.01551898  248.00724634
  237.99964135  227.99270392  217.986434    207.98083151  197.97589639
  187.97162857  177.96802798  167.96509456  157.96282825  147.96122897
  137.96029665  127.96003124  117.96043267  107.96150086   97.96323575
   87.96563727   77.96870537   67.97243996   57.97684099   47.98190839
   37.98764209   27.99404202   18.00110812    8.00884032   -1.98276144
  -11.97369723  -21.96396713  -31.95357119  -41.94250948  -51.93078206
  -61.91838902  -71.9053304   -81.89160628  -91.87721673 -101.86216181
 -111.84644158 -121.83005612 -131.81300549 -141.79528975 -151.77690898
 -161.75786324] km / s
brightest_cube unmasked values:  [[ 3.96811520e-05 -8.41866713e-05 -7.88558391e-05 ... -3.87204345e-04
  -5.62617672e-04 -5.18065877e-04]
 [ 4.96062567e-05 -4.27419436e-05 -1.57168834e-05 ... -5.08279598e-04
  -6.71427115e-04 -7.07802130e-04]
 [ 5.01758186e-05  7.55995279e-06 -6.29120623e-05 ... -5.45957300e-04
  -7.76681176e-04 -7.66127894e-04]
 ...
 [ 4.02163831e-04  3.15971964e-04  1.90121267e-04 ...  3.12264514e-04
   4.26833227e-04  6.56572112e-04]
 [ 4.44979232e-04  2.97025108e-04  1.76950940e-04 ...  1.40180746e-05
   2.56236293e-04  4.44111705e-04]
 [ 2.50054116e-04  1.91611151e-04 -1.24303915e-05 ...  8.32177466e-06
   1.18798533e-04  3.42549465e-04]] Jy / beam
WARNING: PossiblySlowWarning: This function (<function BaseSpectralCube.argmax at 0x19e905a60>) requires loading the entire cube into memory and may therefore be slow. [spectral_cube.utils]
Peak Velocity:  [[638.82525781 638.82525781 638.82525781 ... 638.82525781 638.82525781
  638.82525781]
 [638.82525781 638.82525781 638.82525781 ... 638.82525781 638.82525781
  638.82525781]
 [638.82525781 638.82525781 638.82525781 ... 638.82525781 638.82525781
  638.82525781]
 ...
 [638.82525781 638.82525781 638.82525781 ... 638.82525781 638.82525781
  638.82525781]
 [638.82525781 638.82525781 638.82525781 ... 638.82525781 638.82525781
  638.82525781]
 [638.82525781 638.82525781 638.82525781 ... 638.82525781 638.82525781
  638.82525781]] km / s
All NaN Slice Here?
WARNING: PossiblySlowWarning: This function (<function BaseSpectralCube.max at 0x19e905700>) requires loading the entire cube into memory and may therefore be slow. [spectral_cube.utils]
/Users/jmangum/anaconda3/envs/python39/lib/python3.9/site-packages/spectral_cube/spectral_cube.py:441: RuntimeWarning: All-NaN slice encountered
  out = function(self._get_filled_data(fill=fill,
max_map:  [[nan nan nan ... nan nan nan]
 [nan nan nan ... nan nan nan]
 [nan nan nan ... nan nan nan]
 ...
 [nan nan nan ... nan nan nan]
 [nan nan nan ... nan nan nan]
 [nan nan nan ... nan nan nan]] Jy / beam
...maybe not...

Not at all clear to me why the max_map calculation is producing an all-NaN array when the brightest_cube appears to have reasonable values.

I think that I understand now (though there still might be a bug). The following:

    max_map = peak_amplitude = brightest_cube.max(axis=0) # This is sometimes as all-NaN slice
    print('shape(max_map): ',max_map.shape)
    print('type(max_map): ',type(max_map))
    print('max_map.max (all dims): ',max_map.max())
    print('np.nanmax(max_map) (all dims): ',np.nanmax(max_map))

...produces the following output:

/Users/jmangum/anaconda3/envs/python39/lib/python3.9/site-packages/spectral_cube/spectral_cube.py:441: RuntimeWarning: All-NaN slice encountered
  out = function(self._get_filled_data(fill=fill,
shape(max_map):  (334, 400)
type(max_map):  <class 'spectral_cube.lower_dimensional_structures.Projection'>
max_map.max (all dims):  nan Jy / beam
np.nanmax(max_map) (all dims):  0.16510973870754242 Jy / beam

This makes me suspect that two things are (possibly) happening:
(1) The calculation of max_map, which produces the maximum spectral value for each pixel position, is correct. Some pixel positions are completely blanked, which means that all spectral values are NaN, while others have a mixture of real and NaN values. The warning message says that there was an All-NaN slice encountered, meaning that at least one spectrum toward a pixel was all-NaN, which is correct.
(2) @keflavich Why isn't max_map.max() the same as np.nanmax(max_map)?

Found an additional issue that I will open a separate issue for... max_map is derived from a masked version of cutoutcube, which is the so-called "brightest line cube". max_map is used later in cubelinemoment_multiline to calculate peak_sn = max_map / noisemap. noisemap is derived from cube (the target line cube), not from cutoutcube. This looks inconsistent...

(2) @keflavich Why isn't max_map.max() the same as np.nanmax(max_map)?

max_map.max() is np.max(array) because max_map is not a SpectralCube, it's a Projection

ok. Can close this issue.