Skip to content
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

Initial version of BaseEnhancedTable and PhotometryData added #125

Merged
merged 32 commits into from
Jul 19, 2023

Conversation

JuanCab
Copy link
Contributor

@JuanCab JuanCab commented Jun 30, 2023

Fixes #114 and fixes #115.

I added an initial version of the BaseEnhancedTable and PhotometryData classes to the core.py file and added extensive tests to the tests/test_core.py.

Please note while BaseEnhancedTable does force certain attributes to exist, the PhotometryData did NOT add any required attributes. Its easy enough to add, I just wasn't sure what you wanted to require as being there.

@JuanCab JuanCab added the New base/data classes Develop a base class for the new `astropy.table.Table`+headers class label Jun 30, 2023
@JuanCab JuanCab requested a review from mwcraig June 30, 2023 01:19
@JuanCab JuanCab marked this pull request as ready for review June 30, 2023 16:57
Copy link
Contributor

@mwcraig mwcraig left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sure I'll have more to say -- I'm going to look over the metaprogramming section of fluent python to see if there is a different approach to take instead of subclassing.

CHANGES.rst Outdated
@@ -3,12 +3,15 @@

New Features
^^^^^^^^^^^^
+ Continued fleshing out of the documentation.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think we need this. The new stuff has documentation but that should be the norm going forward.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noted.

CHANGES.rst Outdated Show resolved Hide resolved
stellarphot/core.py Outdated Show resolved Hide resolved
stellarphot/core.py Outdated Show resolved Hide resolved
stellarphot/core.py Outdated Show resolved Hide resolved
stellarphot/core.py Outdated Show resolved Hide resolved
stellarphot/core.py Outdated Show resolved Hide resolved
stellarphot/tests/test_core.py Outdated Show resolved Hide resolved
stellarphot/tests/test_core.py Show resolved Hide resolved
stellarphot/tests/test_core.py Outdated Show resolved Hide resolved
@JuanCab
Copy link
Contributor Author

JuanCab commented Jun 30, 2023

Well let me know. I looked at @dataclasses but subclassing data classes looked... painful and it forced an order to parameters I was unhappy with.

Copy link
Contributor Author

@JuanCab JuanCab left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went through your comments and tried to address most of them.

# Check type
if data[this_col].dtype != coltypes[i]:
raise ValueError(f"data[{this_col}] is of wrong type (declared {coltypes[i]} but reported as {data[this_col].dtype}).")
if data[this_col].unit != colunits[i]:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'is not' is checking for identity being the same, e.g. they are the SAME OBJECT, != checks for equality. I suspect it fails with 1/adu because that is a constructed unit and so two different instances of it are considered two different objects. An equality check seems to be the way to go.

stellarphot/core.py Outdated Show resolved Hide resolved
stellarphot/core.py Outdated Show resolved Hide resolved
super().__init__(self.phot_descript, data)


def update_filters(self):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I think we'll need to allow for the possibility that there is not a corresponding filter, eg 540_bp40.

Well, the way the code currently currently works, if the filter is not mentioned in the filter_map, the filter will simply not be changed. So the filter_map should only be for those filters that do map to standard AAVSO names.

stellarphot/tests/test_core.py Outdated Show resolved Hide resolved
Copy link
Contributor Author

@JuanCab JuanCab left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went through your comments and tried to address most of them.

@mwcraig
Copy link
Contributor

mwcraig commented Jul 9, 2023

I'm not 100% sure the discussion about types went off to, but

Can you confirm if you subsequently add new data to the text column that is longer than the initial allocation, the string length gets updated?

The answer seems to be complicated. If you start with an empty table the only way to populate it seems to be add_row or add_rows. If you add data that way, then the string column length gets changed automatically.

If the table already has data then what happens if you try to update a string column depend ons on how you do it.

If you create the table and add a row like this

from astropy.table import Table
t = Table(names=['decimal', 'text'], dtype=('float', 'str'))
t.add_row((10.0, "hello"))

then t["text"] is string of length 5.

Updating like this changes to length 7:

t["text"] = ["longest'}

but if you instead try this it truncates:

t["text"][:] = ["longest"]

I think the reason is that t["text"] = ... is really replacing the original column, not simply updating the values The second case updates the vlaues but also warns that they are being truncated.

@mwcraig
Copy link
Contributor

mwcraig commented Jul 10, 2023

Something else just occurred to me -- changing an attribute doesn't change the underlying table, so an attribute and its corresponding table column can get out of sync.

Not sure how to deal with this; one option might be to make properties instead of settable attributes.

To reproduce, run this, and note that at the end bet.number and bet.data["number"] are different:

import numpy as np
from astropy.table import Table

table_description = np.array([["number", float, None, "number"],
    ["ra", float, "degree", "ra"],
    ["dec", float, "degree", "dec"],
    ["id", int, None, "id"]
])

tab = Table(data=[[1], ["abc"], [1.0], [1.0], [5.1]], names=("id", "tex
    ...: t", "ra", "dec", "number"), units=(None, None, "degree", "degree", None
    ...: ))

bet = BaseEnhancedTable(table_description, data=tab)

bet.number

# Returns
# <Column name='number' dtype='float64' length=1>
# 5.1

bet.number = [6.1]
bet.number

# Returns
# [6.1]

bet.data["number"]

# Returns
# <Column name='number' dtype='float64' length=1>
# 5.1

@JuanCab
Copy link
Contributor Author

JuanCab commented Jul 10, 2023

Something else just occurred to me -- changing an attribute doesn't change the underlying table, so an attribute and its corresponding table column can get out of sync.

Not sure how to deal with this; one option might be to make properties instead of settable attributes.

To reproduce, run this, and note that at the end bet.number and bet.data["number"] are different:

import numpy as np
from astropy.table import Table

table_description = np.array([["number", float, None, "number"],
    ["ra", float, "degree", "ra"],
    ["dec", float, "degree", "dec"],
    ["id", int, None, "id"]
])

tab = Table(data=[[1], ["abc"], [1.0], [1.0], [5.1]], names=("id", "tex
    ...: t", "ra", "dec", "number"), units=(None, None, "degree", "degree", None
    ...: ))

bet = BaseEnhancedTable(table_description, data=tab)

bet.number

# Returns
# <Column name='number' dtype='float64' length=1>
# 5.1

bet.number = [6.1]
bet.number

# Returns
# [6.1]

bet.data["number"]

# Returns
# <Column name='number' dtype='float64' length=1>
# 5.1

This doesn’t surprise me, you can take any variable and replace its value with a new one. I just created an attribute that pointed to a astropy table column. Resetting that attribute to a list breaks that pointer.

[edited] As you suggested, we might be able to handle this by decorating it with @property and adding the corresponding getter/setter/deleter functions. The only trick is constructing functions with names based on the columns in a dynamic way. Essentially we would have to create template functions and then assign attributes to the functions, not sure if that will work.

@JuanCab
Copy link
Contributor Author

JuanCab commented Jul 16, 2023

I have uploaded a complete rewrite pair of data classes constructed based on feedback from Astropy folks at SciPy2023. This is what caused so much grumbling during the sprints, but it is done.

@JuanCab
Copy link
Contributor Author

JuanCab commented Jul 17, 2023

On the flight I hit on the solution for computing the ‘night’, so that’s taken care of as well.

Copy link
Contributor

@mwcraig mwcraig left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent progress! There are a couple suggestions inline for some changes. We should also talk briefly about how data gets added to this structure by stellarphot as it is doing photometry.

stellarphot/core.py Outdated Show resolved Hide resolved
stellarphot/core.py Outdated Show resolved Hide resolved
stellarphot/core.py Outdated Show resolved Hide resolved
raise ValueError(f"self.data should exist at this point, but doesn't, crap.")

# Confirm a proper table description is passed
if not isinstance(self._table_description, dict):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need to be this restrictive. The only thing we really need is that table_description is accessible via keys. We may want to make sure this is a dictionary for us, so we could do something like this up where you currently set self._table_description = table_description (lines below are untested, meant to illustrate the idea)

try:
    self._table_description = {k: v for k, v in table_description.items()}
except AttributeError:
    raise TypeError(...)

stellarphot/core.py Outdated Show resolved Hide resolved
stellarphot/core.py Outdated Show resolved Hide resolved
stellarphot/core.py Outdated Show resolved Hide resolved
stellarphot/core.py Outdated Show resolved Hide resolved
stellarphot/core.py Show resolved Hide resolved
stellarphot/core.py Outdated Show resolved Hide resolved
Copy link
Contributor Author

@JuanCab JuanCab left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just discovered the required column checking I thought was being done is not, so still need to debug this.

stellarphot/core.py Outdated Show resolved Hide resolved
stellarphot/core.py Outdated Show resolved Hide resolved
stellarphot/core.py Outdated Show resolved Hide resolved
stellarphot/core.py Outdated Show resolved Hide resolved
stellarphot/core.py Outdated Show resolved Hide resolved
stellarphot/core.py Outdated Show resolved Hide resolved
stellarphot/core.py Outdated Show resolved Hide resolved
stellarphot/core.py Outdated Show resolved Hide resolved
stellarphot/core.py Show resolved Hide resolved
stellarphot/core.py Outdated Show resolved Hide resolved
@mwcraig mwcraig merged commit 4c835b9 into feder-observatory:main Jul 19, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
New base/data classes Develop a base class for the new `astropy.table.Table`+headers class
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Write Photometry class that uses new base clase New base class that is Table plus header/attributes
2 participants