Repository: hgrecco/pint-pandas (language: python)

Issue:
`PintArray` should accept Quantity arrays directly
Given a Quantity array `q`, the correct way to create a `PintArrray` currently is to call `PintArray(q.magnitude, q.units)`.
One may be tempted to instead call `PintArray(q, q.units)` instead, which gives the correct result, but causes a `UnitsStrippedWarning` to be raised. Either way, it would be much more convenient to simply call `PintArray(q)`.

Furthermore, one may instead expect `PintArray(q, some_unit)` to do the right thing(TM) (convert to `some_unit` if possible, otherwise fail loudly) - however, the units are just silently being replaced.


Discussion / hints:
Relatedly, accepting/interpreting lists/dicts of Quantities in the `DataFrame` constructor methods (namely `pandas.DataFrame.from_dict()` and `pandas.DataFrame.from_records()`) would also be awesome. 

e.g. given a `list[dict]` where each list entry is a row and the dict keys are columns:

```
inlist = [
{ 'power': 8*ureg.W }, 
{ 'power': 16*ureg.W}
]
```

It would be great if `df = pd.DataFrame.from_records(inlist)` would make the 'power' column of type `pint[Watt]` instead of the current `object`
Yes, this makes sense. Next on the to do list. Good spot on the unit conversion bug.
> Relatedly, accepting/interpreting lists/dicts of Quantities in the `DataFrame` constructor methods (namely `pandas.DataFrame.from_dict()` and `pandas.DataFrame.from_records()`) would also be awesome.
> 
> e.g. given a `list[dict]` where each list entry is a row and the dict keys are columns:
> 
> ```
> inlist = [
> { 'power': 8*ureg.W }, 
> { 'power': 16*ureg.W}
> ]
> ```
> 
> It would be great if `df = pd.DataFrame.from_records(inlist)` would make the 'power' column of type `pint[Watt]` instead of the current `object`

Not something pint-pandas has control over, you'd need to make a request to pandas.

`pd.DataFrame.from_records` accepts a dtype argument so you could do `dtype = 'pint[W]' `
I'd guess that the place to start with Pandas would be pandas.DataFrame.convert_dtypes (and in particular to make Quantities one of the fundamental things that Pandas respects, like ints, floats, strings, and booleans).  They offer the following invitation:

```
In the future, as new dtypes are added that support pd.NA, the results of this method will change to support those new dtypes.
```

Fix patch:
diff --git a/CHANGES b/CHANGES
index 02d0c86..c53f686 100644
--- a/CHANGES
+++ b/CHANGES
@@ -8,6 +8,8 @@ pint-pandas Changelog
   to convert input arrays to lists of Quantity, thereby causing a huge slowdown. #80
 - Fixed a bug where listlike operands in binary operations were not correctly converted to Quantity arrays. #86
 - Fixed pickling of `PintArray` instances. #71
+- Fixed a bug where units of a quantity where ignored when creating a PintArray. #104
+- Quantity arrays may be used to initialise PintArrays without specifying the dtype. #104
 
 0.2 (2021-03-23)
 ----------------
diff --git a/pint_pandas/pint_array.py b/pint_pandas/pint_array.py
index 9c418ca..a707580 100644
--- a/pint_pandas/pint_array.py
+++ b/pint_pandas/pint_array.py
@@ -179,17 +179,40 @@ def __repr__(self):
 
 
 class PintArray(ExtensionArray, ExtensionOpsMixin):
+    """Implements a class to describe an array of physical quantities:
+    the product of an array of numerical values and a unit of measurement.
+
+    Parameters
+    ----------
+    values : pint.Quantity or array-like
+        Array of physical quantity values to be created.
+    dtype : PintType, str, or pint.Unit
+        Units of the physical quantity to be created. (Default value = None)
+        When values is a pint.Quantity, passing None as the dtype will use
+        the units from the pint.Quantity.
+    copy: bool
+        Whether to copy the values.
+    Returns
+    -------
+
+    """
+
     _data = np.array([])
     context_name = None
     context_units = None
 
     def __init__(self, values, dtype=None, copy=False):
+        if dtype is None and isinstance(values, _Quantity):
+            dtype = values.units
         if dtype is None:
             raise NotImplementedError
 
         if not isinstance(dtype, PintType):
             dtype = PintType(dtype)
         self._dtype = dtype
+
+        if isinstance(values, _Quantity):
+            values = values.to(dtype.units).magnitude
         if not isinstance(values, np.ndarray):
             values = np.array(values, copy=copy)
             copy = False


Test patch:
diff --git a/pint_pandas/testsuite/test_issues.py b/pint_pandas/testsuite/test_issues.py
index 787e24e..d059314 100644
--- a/pint_pandas/testsuite/test_issues.py
+++ b/pint_pandas/testsuite/test_issues.py
@@ -4,6 +4,7 @@
 import numpy as np
 import pandas as pd
 import pytest
+from pint.testsuite import helpers
 
 import pint_pandas as ppi
 from pint_pandas import PintArray
@@ -79,3 +80,13 @@ def test_issue_71():
     s = pickle.dumps(a)
     b = pickle.loads(s)
     assert np.all(a == b)
+
+
+def test_issue_88():
+    q_m = ureg.Quantity([1, 2], "m")
+    a = PintArray(q_m)
+    helpers.assert_quantity_almost_equal(q_m, a.quantity)
+
+    q_mm = ureg.Quantity([1000, 2000], "mm")
+    b = PintArray(q_mm, "m")
+    helpers.assert_quantity_almost_equal(q_m, b.quantity)