diff options
-rw-r--r-- | AUTHORS | 2 | ||||
-rw-r--r-- | notebooks/pandas-support.ipynb | 90 | ||||
-rw-r--r-- | pint/compat/__init__.py | 5 | ||||
-rw-r--r-- | pint/pandas_interface/pint_array.py | 29 | ||||
-rw-r--r-- | pint/testsuite/test-data/pandas_test.csv | 8 | ||||
-rw-r--r-- | pint/testsuite/test_pandas_interface.py | 50 |
6 files changed, 123 insertions, 61 deletions
@@ -6,6 +6,7 @@ Other contributors, listed alphabetically, are: * Alexander Böhn <fish2000@gmail.com> * Ana Krivokapic <akrivokapic1@gmail.com> * Andrea Zonca <code@andreazonca.com> +* Andrew Savage <andrewgsavage@gmail.com> * Brend Wanders <b.wanders@utwente.nl> * choloepus * coutinho <coutinho@esrf.fr> @@ -38,5 +39,6 @@ Other contributors, listed alphabetically, are: * Thomas Kluyver <takowl@gmail.com> * Tom Ritchford <tom@swirly.com> * Virgil Dupras <virgil.dupras@savoirfairelinux.com> +* Zebedee Nicholls <zebedee.nicholls@climate-energy-college.org> (If you think that your name belongs here, please let the maintainer know) diff --git a/notebooks/pandas-support.ipynb b/notebooks/pandas-support.ipynb index bd0a251..9900896 100644 --- a/notebooks/pandas-support.ipynb +++ b/notebooks/pandas-support.ipynb @@ -944,7 +944,7 @@ " <th>fluid power</th>\n", " </tr>\n", " <tr>\n", - " <th></th>\n", + " <th>unit</th>\n", " <th>revolutions_per_minute</th>\n", " <th>meter * newton * revolutions_per_minute</th>\n", " <th>meter * newton</th>\n", @@ -995,19 +995,19 @@ "</div>" ], "text/plain": [ - " speed mech power \\\n", - " revolutions_per_minute meter * newton * revolutions_per_minute \n", - "0 1000.0 10000.0 \n", - "1 1100.0 11000.0 \n", - "2 1200.0 12000.0 \n", - "3 1200.0 12000.0 \n", + " speed mech power \\\n", + "unit revolutions_per_minute meter * newton * revolutions_per_minute \n", + "0 1000.0 10000.0 \n", + "1 1100.0 11000.0 \n", + "2 1200.0 12000.0 \n", + "3 1200.0 12000.0 \n", "\n", - " torque rail pressure fuel flow rate fluid power \n", - " meter * newton bar liter / minute bar * liter / minute \n", - "0 10.0 1.000000e+03 10.0 1.000000e+04 \n", - "1 10.0 1.000000e+12 10.0 1.000000e+13 \n", - "2 10.0 1.000000e+03 10.0 1.000000e+04 \n", - "3 10.0 1.000000e+03 10.0 1.000000e+04 " + " torque rail pressure fuel flow rate fluid power \n", + "unit meter * newton bar liter / minute bar * liter / minute \n", + "0 10.0 1.000000e+03 10.0 1.000000e+04 \n", + "1 10.0 1.000000e+12 10.0 1.000000e+13 \n", + "2 10.0 1.000000e+03 10.0 1.000000e+04 \n", + "3 10.0 1.000000e+03 10.0 1.000000e+04 " ] }, "execution_count": 17, @@ -1060,7 +1060,7 @@ " <th>fluid power</th>\n", " </tr>\n", " <tr>\n", - " <th></th>\n", + " <th>unit</th>\n", " <th>revolutions_per_minute</th>\n", " <th>kilowatt</th>\n", " <th>meter * newton</th>\n", @@ -1111,19 +1111,19 @@ "</div>" ], "text/plain": [ - " speed mech power torque rail pressure \\\n", - " revolutions_per_minute kilowatt meter * newton bar \n", - "0 1000.0 1.047198 10.0 1.000000e+03 \n", - "1 1100.0 1.151917 10.0 1.000000e+12 \n", - "2 1200.0 1.256637 10.0 1.000000e+03 \n", - "3 1200.0 1.256637 10.0 1.000000e+03 \n", + " speed mech power torque rail pressure \\\n", + "unit revolutions_per_minute kilowatt meter * newton bar \n", + "0 1000.0 1.047198 10.0 1.000000e+03 \n", + "1 1100.0 1.151917 10.0 1.000000e+12 \n", + "2 1200.0 1.256637 10.0 1.000000e+03 \n", + "3 1200.0 1.256637 10.0 1.000000e+03 \n", "\n", - " fuel flow rate fluid power \n", - " liter / minute kilowatt \n", - "0 10.0 1.666667e+01 \n", - "1 10.0 1.666667e+10 \n", - "2 10.0 1.666667e+01 \n", - "3 10.0 1.666667e+01 " + " fuel flow rate fluid power \n", + "unit liter / minute kilowatt \n", + "0 10.0 1.666667e+01 \n", + "1 10.0 1.666667e+10 \n", + "2 10.0 1.666667e+01 \n", + "3 10.0 1.666667e+01 " ] }, "execution_count": 18, @@ -1178,7 +1178,7 @@ " <th>fluid power</th>\n", " </tr>\n", " <tr>\n", - " <th></th>\n", + " <th>unit</th>\n", " <th>radian / second</th>\n", " <th>kilogram * meter ** 2 / second ** 3</th>\n", " <th>kilogram * meter ** 2 / second ** 2</th>\n", @@ -1229,26 +1229,26 @@ "</div>" ], "text/plain": [ - " speed mech power \\\n", - " radian / second kilogram * meter ** 2 / second ** 3 \n", - "0 104.719755 1047.197551 \n", - "1 115.191731 1151.917306 \n", - "2 125.663706 1256.637061 \n", - "3 125.663706 1256.637061 \n", + " speed mech power \\\n", + "unit radian / second kilogram * meter ** 2 / second ** 3 \n", + "0 104.719755 1047.197551 \n", + "1 115.191731 1151.917306 \n", + "2 125.663706 1256.637061 \n", + "3 125.663706 1256.637061 \n", "\n", - " torque rail pressure \\\n", - " kilogram * meter ** 2 / second ** 2 kilogram / meter / second ** 2 \n", - "0 10.0 1.000000e+08 \n", - "1 10.0 1.000000e+17 \n", - "2 10.0 1.000000e+08 \n", - "3 10.0 1.000000e+08 \n", + " torque rail pressure \\\n", + "unit kilogram * meter ** 2 / second ** 2 kilogram / meter / second ** 2 \n", + "0 10.0 1.000000e+08 \n", + "1 10.0 1.000000e+17 \n", + "2 10.0 1.000000e+08 \n", + "3 10.0 1.000000e+08 \n", "\n", - " fuel flow rate fluid power \n", - " meter ** 3 / second kilogram * meter ** 2 / second ** 3 \n", - "0 0.000167 1.666667e+04 \n", - "1 0.000167 1.666667e+13 \n", - "2 0.000167 1.666667e+04 \n", - "3 0.000167 1.666667e+04 " + " fuel flow rate fluid power \n", + "unit meter ** 3 / second kilogram * meter ** 2 / second ** 3 \n", + "0 0.000167 1.666667e+04 \n", + "1 0.000167 1.666667e+13 \n", + "2 0.000167 1.666667e+04 \n", + "3 0.000167 1.666667e+04 " ] }, "execution_count": 19, diff --git a/pint/compat/__init__.py b/pint/compat/__init__.py index f2ea30b..4afdf75 100644 --- a/pint/compat/__init__.py +++ b/pint/compat/__init__.py @@ -127,7 +127,10 @@ if not HAS_PROPER_BABEL: try: import pandas as pd HAS_PANDAS = True - HAS_PROPER_PANDAS = hasattr(pd.core.arrays.base, "ExtensionOpsMixin") + HAS_PROPER_PANDAS = ( + hasattr(pd.core, "arrays") + and hasattr(pd.core.arrays.base, "ExtensionOpsMixin") + ) except ImportError: HAS_PROPER_PANDAS = HAS_PANDAS = False diff --git a/pint/pandas_interface/pint_array.py b/pint/pandas_interface/pint_array.py index 42ebe65..f53c27f 100644 --- a/pint/pandas_interface/pint_array.py +++ b/pint/pandas_interface/pint_array.py @@ -3,8 +3,6 @@ pint.pandas_interface.pint_array ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # I'm happy with both of these as long as Andrew and Zebedee are added on - # but need to check with Pint Authors... :copyright: 2018 by Pint Authors, see AUTHORS for more details. :license: BSD, see LICENSE for more details. """ @@ -500,21 +498,34 @@ class PintDataFrameAccessor(object): unit_col_name = df_columns.columns[level] units = df_columns[unit_col_name] df_columns = df_columns.drop(columns=unit_col_name) - df_columns.values - df_new = DataFrame({i: PintArray(Q_(df.values[:, i], unit)) + + df_new = DataFrame({ + i: PintArray(Q_(df.values[:, i], unit)) for i, unit in enumerate(units.values) }) + df_new.columns = df_columns.index.droplevel(unit_col_name) df_new.index = df.index + return df_new def dequantify(self): - df=self._obj - df_columns=df.columns.to_frame() - df_columns['units']=[str(df[col].values.data.units) for col in df.columns] - df_new=DataFrame({ tuple(df_columns.iloc[i]) : df[col].values.data.magnitude - for i,col in enumerate(df.columns) + df = self._obj + + df_columns = df.columns.to_frame() + df_columns['units'] = [ + str(df[col].values.data.units) + for col in df.columns + ] + + df_new = DataFrame({ + tuple(df_columns.iloc[i]): df[col].values.data.magnitude + for i, col in enumerate(df.columns) }) + + df_new.columns.names = df.columns.names + ['unit'] + df_new.index = df.index + return df_new def to_base_units(self): diff --git a/pint/testsuite/test-data/pandas_test.csv b/pint/testsuite/test-data/pandas_test.csv index 93d912c..5301e81 100644 --- a/pint/testsuite/test-data/pandas_test.csv +++ b/pint/testsuite/test-data/pandas_test.csv @@ -1,6 +1,6 @@ speed,mech power,torque,rail pressure,fuel flow rate,fluid power rpm,kW,N m,bar,l/min,kW -1000,,10,1000,10, -1100,,10,1000000000000,10, -1200,,10,1000,10, -1200,,10,1000,10, +1000.0,,10.0,1000.0,10.0, +1100.0,,10.0,1000000000000.0,10.0, +1200.0,,10.0,1000.0,10.0, +1200.0,,10.0,1000.0,10.0, diff --git a/pint/testsuite/test_pandas_interface.py b/pint/testsuite/test_pandas_interface.py index b4a7fed..0da478e 100644 --- a/pint/testsuite/test_pandas_interface.py +++ b/pint/testsuite/test_pandas_interface.py @@ -421,7 +421,7 @@ else: "test-data", "pandas_test.csv" ) - df=pd.read_csv(test_csv, header=[0,1]) + df = pd.read_csv(test_csv, header=[0,1]) df_ = df.pint.quantify(ureg, level=-1) df_['mech power'] = df_.speed*df_.torque @@ -436,6 +436,53 @@ else: df_.pint.to_base_units().pint.dequantify() + class TestDataFrameAccessor(object): + def test_index_maintained(self): + test_csv = join( + dirname(__file__), + "test-data", "pandas_test.csv" + ) + + df = pd.read_csv(test_csv, header=[0, 1]) + df.columns = pd.MultiIndex.from_arrays( + [ + ['Holden', 'Holden', 'Holden', 'Ford', 'Ford', 'Ford'], + ['speed', 'mech power', 'torque', 'rail pressure', 'fuel flow rate' ,'fluid power'], + ['rpm', 'kW', 'N m', 'bar', 'l/min', 'kW'], + ], + names = ['Car type', 'metric', 'unit'] + ) + df.index = pd.MultiIndex.from_arrays( + [ + [1, 12, 32, 48], + ['Tim', 'Tim', 'Jane', 'Steve'], + ], + names = ['Measurement number', 'Measurer'] + + ) + + + expected = df.copy() + + # we expect the result to come back with pint names, not input + # names + def get_pint_value(in_str): + return str(ureg.Quantity(1, in_str).units) + + units_level = [ + i for i, name in enumerate(df.columns.names) if name == 'unit' + ][0] + + expected.columns = df.columns.set_levels( + df.columns.levels[units_level].map(get_pint_value), + level='unit' + ) + + + result = df.pint.quantify(ureg, level=-1).pint.dequantify() + + pd.testing.assert_frame_equal(result, expected) + class TestSeriesAccessors(object): @pytest.mark.parametrize('attr', [ @@ -530,7 +577,6 @@ else: ] - class TestPintArrayQuantity(QuantityTestCase): FORCE_NDARRAY = True |