Próbuję skonfigurować podklasę, pd.DataFrame
która ma dwa wymagane argumenty podczas inicjowania ( group
i timestamp_col
). Chcę uruchomić sprawdzanie na te argumenty group
i timestamp_col
tak mam metody setter dla każdej z właściwości. To wszystko działa, dopóki nie spróbować set_index()
i dostać TypeError: 'NoneType' object is not iterable
. Wygląda na to, że żaden argument nie jest przekazywany do mojej funkcji ustawiającej w test_set_index
i test_assignment_with_indexed_obj
. Jeśli dodam if g == None: return
do mojej funkcji ustawiającej, mogę zdać przypadki testowe, ale nie sądzę, że jest to właściwe rozwiązanie.
Jak wdrożyć sprawdzanie poprawności właściwości dla tych wymaganych argumentów?
Poniżej jest moja klasa:
import pandas as pd
import numpy as np
class HistDollarGains(pd.DataFrame):
@property
def _constructor(self):
return HistDollarGains._internal_ctor
_metadata = ["group", "timestamp_col", "_group", "_timestamp_col"]
@classmethod
def _internal_ctor(cls, *args, **kwargs):
kwargs["group"] = None
kwargs["timestamp_col"] = None
return cls(*args, **kwargs)
def __init__(
self,
data,
group,
timestamp_col,
index=None,
columns=None,
dtype=None,
copy=True,
):
super(HistDollarGains, self).__init__(
data=data, index=index, columns=columns, dtype=dtype, copy=copy
)
self.group = group
self.timestamp_col = timestamp_col
@property
def group(self):
return self._group
@group.setter
def group(self, g):
if g == None:
return
if isinstance(g, str):
group_list = [g]
else:
group_list = g
if not set(group_list).issubset(self.columns):
raise ValueError("Data does not contain " + '[' + ', '.join(group_list) + ']')
self._group = group_list
@property
def timestamp_col(self):
return self._timestamp_col
@timestamp_col.setter
def timestamp_col(self, t):
if t == None:
return
if not t in self.columns:
raise ValueError("Data does not contain " + '[' + t + ']')
self._timestamp_col = t
Oto moje przypadki testowe:
import pytest
import pandas as pd
import numpy as np
from myclass import *
@pytest.fixture(scope="module")
def sample():
samp = pd.DataFrame(
[
{"timestamp": "2020-01-01", "group": "a", "dollar_gains": 100},
{"timestamp": "2020-01-01", "group": "b", "dollar_gains": 100},
{"timestamp": "2020-01-01", "group": "c", "dollar_gains": 110},
{"timestamp": "2020-01-01", "group": "a", "dollar_gains": 110},
{"timestamp": "2020-01-01", "group": "b", "dollar_gains": 90},
{"timestamp": "2020-01-01", "group": "d", "dollar_gains": 100},
]
)
return samp
@pytest.fixture(scope="module")
def sample_obj(sample):
return HistDollarGains(sample, "group", "timestamp")
def test_constructor_without_args(sample):
with pytest.raises(TypeError):
HistDollarGains(sample)
def test_constructor_with_string_group(sample):
hist_dg = HistDollarGains(sample, "group", "timestamp")
assert hist_dg.group == ["group"]
assert hist_dg.timestamp_col == "timestamp"
def test_constructor_with_list_group(sample):
hist_dg = HistDollarGains(sample, ["group", "timestamp"], "timestamp")
def test_constructor_with_invalid_group(sample):
with pytest.raises(ValueError):
HistDollarGains(sample, "invalid_group", np.random.choice(sample.columns))
def test_constructor_with_invalid_timestamp(sample):
with pytest.raises(ValueError):
HistDollarGains(sample, np.random.choice(sample.columns), "invalid_timestamp")
def test_assignment_with_indexed_obj(sample_obj):
b = sample_obj.set_index(sample_obj.group + [sample_obj.timestamp_col])
def test_set_index(sample_obj):
# print(isinstance(a, pd.DataFrame))
assert sample_obj.set_index(sample_obj.group + [sample_obj.timestamp_col]).index.names == ['group', 'timestamp']
python
pandas
properties
subclass
strona
źródło
źródło
None
wartośćgroup
nieruchomości jest nieprawidłowa , czy nie powinieneś podnieść wartościValueError
?None
to jest niepoprawna wartość, dlatego nie podoba mi się instrukcja if. Ale dodanie, że Brak, sprawia, że przechodzi testy. Szukam sposobu prawidłowego rozwiązania tego problemu bez instrukcji None if.ValueError
. Problem polega na ustaleniu, na czym polega próba ustawieniagroup
atrybutuNone
.Odpowiedzi:
set_index()
Metoda wezwieself.copy()
wewnętrznie, aby utworzyć kopię obiektu DataFrame (patrz kod źródłowy tutaj ), wewnątrz którego używa dostosowany konstruktora,_internal_ctor()
aby utworzyć nowy obiekt ( źródło ). Zauważ, żeself._constructor()
jest identyczny zself._internal_ctor()
, który jest powszechną metodą wewnętrzną dla prawie wszystkich klas pand do tworzenia nowych instancji podczas operacji takich jak głębokie kopiowanie lub krojenie. Twój problem pochodzi z tej funkcji:Myślę, że skopiowałeś ten kod z wydania github . Linie
kwargs["**"] = None
wyraźnie nakazują konstruktorowi ustawienieNone
zarówno na, jakgroup
i natimestamp_col
. W końcu setter / validator otrzymujeNone
nową wartość i zgłasza błąd.Dlatego należy ustawić dopuszczalną wartość na
group
itimestamp_col
.Następnie możesz usunąć
if g == None: return
linie z walidatora.źródło