Skip to content

Commit

Permalink
Merge #58
Browse files Browse the repository at this point in the history
58: [WIP] Convert object dtype r=andrewgsavage a=andrewgsavage

Addresses #55

Co-authored-by: andrewgsavage <andrewgsavage@gmail.com>
Co-authored-by: Andrew <andrewgsavage@gmail.com>
  • Loading branch information
bors[bot] and andrewgsavage authored Jun 24, 2023
2 parents 958e36c + 5b2b2d4 commit bf84e37
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 5 deletions.
4 changes: 4 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ pint-pandas Changelog
0.5 (unreleased)
----------------

<<<<<<< HEAD
- Support for <NA> values in columns with integer magnitudes
- Support for magnitudes of any type, such as complex128 or tuples #146
- Support for pandas 2.0, allowing `.cumsum, .cummax, .cummin` methods for `Series` and `DataFrame`. #186
- Minimum Pint version is 0.21
- Minimum Pandas vesrion is 2.0
- Support for unit registries with `force_ndarray_like = True`. #165
- A DataFrame/Series.pint.convert_object_dtype() function has been added to create PintArrays from Series of quantities.

0.4 (2023-05-23)
----------------
Expand Down
40 changes: 35 additions & 5 deletions pint_pandas/pint_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -948,15 +948,32 @@ def to_base_units(self):
index=index,
)

def convert_object_dtype(self):
df = self._obj
df_new = pd.DataFrame()
for col in df.columns:
s = df[col]
if s.dtype == "object":
try:
df_new[col] = s.pint.convert_object_dtype()
except AttributeError:
df_new[col] = s
else:
df_new[col] = s
return df_new


@register_series_accessor("pint")
class PintSeriesAccessor(object):
def __init__(self, pandas_obj):
self._validate(pandas_obj)
self.pandas_obj = pandas_obj
self.quantity = pandas_obj.values.quantity
self._index = pandas_obj.index
self._name = pandas_obj.name
if self._is_object_dtype_and_quantity(pandas_obj):
self.pandas_obj = pandas_obj
else:
self._validate(pandas_obj)
self.pandas_obj = pandas_obj
self.quantity = pandas_obj.values.quantity
self._index = pandas_obj.index
self._name = pandas_obj.name

@staticmethod
def _validate(obj):
Expand All @@ -966,6 +983,19 @@ def _validate(obj):
"dtype '{}'.".format(obj.dtype)
)

@staticmethod
def _is_object_dtype_and_quantity(obj):
return obj.dtype == "object" and all(
[(isinstance(item, _Quantity) or pd.isna(item)) for item in obj.values]
)

def convert_object_dtype(self):
return pd.Series(
data=PintArray._from_sequence(self.pandas_obj.values),
index=self.pandas_obj.index,
name=self.pandas_obj.name,
)


class Delegated:
# Descriptor for delegating attribute access to from
Expand Down
10 changes: 10 additions & 0 deletions pint_pandas/testsuite/test_pandas_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,16 @@ def test_series_method_accessors(self, data, attr_args):
s = pd.Series(data)
assert all(getattr(s.pint, attr)(*args) == getattr(data.quantity, attr)(*args))

def test_convert_object_dtype(self, data):
ser = pd.Series(data)
ser_obj = pd.Series(ser.values, dtype="object")
assert ser_obj.pint.convert_object_dtype().dtype == ser.dtype

df = pd.DataFrame({"A": ser, "B": ser})
df2 = pd.DataFrame({"A": ser, "B": ser_obj})

assert all(df2.pint.convert_object_dtype().dtypes == df.dtypes)


arithmetic_ops = [
operator.add,
Expand Down

0 comments on commit bf84e37

Please sign in to comment.