Pracuję z pojedynczymi wierszami ramek danych pand, ale natrafiam na problemy z przymusem podczas indeksowania i wstawiania wierszy. Wydaje się, że Pandy zawsze chcą wymuszać mieszane typy int / float na all-float i nie widzę żadnych oczywistych kontroli tego zachowania.
Na przykład tutaj jest prosta ramka danych z a
jak int
i b
jako float
:
import pandas as pd
pd.__version__ # '0.25.2'
df = pd.DataFrame({'a': [1], 'b': [2.2]})
print(df)
# a b
# 0 1 2.2
print(df.dtypes)
# a int64
# b float64
# dtype: object
Oto problem dotyczący przymusu podczas indeksowania jednego wiersza:
print(df.loc[0])
# a 1.0
# b 2.2
# Name: 0, dtype: float64
print(dict(df.loc[0]))
# {'a': 1.0, 'b': 2.2}
A oto kwestia przymusu podczas wstawiania jednego wiersza:
df.loc[1] = {'a': 5, 'b': 4.4}
print(df)
# a b
# 0 1.0 2.2
# 1 5.0 4.4
print(df.dtypes)
# a float64
# b float64
# dtype: object
W obu przypadkach chcę, aby a
kolumna pozostała typem całkowitym, a nie wymuszona na typ zmiennoprzecinkowy.
df.loc[[0], df.columns]
.read_[type]
obsługuje wiele dtypów ...Odpowiedzi:
Po kilku kopaniach, oto kilka strasznie brzydkich obejść. (Lepsza odpowiedź zostanie zaakceptowana.)
Dziwactwo znalezione tutaj polega na tym, że kolumny nienumeryczne zatrzymują przymus, więc oto jak zindeksować jeden wiersz do
dict
:A wstawianie wiersza można wykonać, tworząc nową ramkę danych z jednym wierszem:
Obie te sztuczki nie są zoptymalizowane dla dużych ramek danych, więc byłbym bardzo wdzięczny za lepszą odpowiedź!
źródło
df['a'] = df.a.astype(mytype)
... Jest jednak wciąż brudny i prawdopodobnie nieskuteczny..astype()
jest niebezpieczny dla float -> liczba całkowita; nie ma problemu ze zmianą1.1
na1
, więc naprawdę musisz upewnić się, że wszystkie twoje wartości są „liczbami całkowitymi” zanim to zrobisz. Prawdopodobnie najlepiej używaćpd.to_numeric
zdowncast='integer'
Źródłem problemu jest to
Widzimy to:
A seria może mieć tylko jeden typ, w twoim przypadku int64 lub float64.
Przychodzą mi do głowy dwa obejścia:
lub
https://github.com/pandas-dev/pandas/blob/master/pandas/core/frame.py#L6973
Więc twoje obejście jest w rzeczywistości solidne, w przeciwnym razie moglibyśmy:
źródło
object
typów danych! Innym jest utworzenie obiektu DataFrame od początku:df = pd.DataFrame({'a': [1], 'b': [2.2]}, dtype=object)
Ilekroć pobierasz dane z ramki danych lub dołączasz dane do ramki danych i musisz zachować ten sam typ danych, unikaj konwersji do innych struktur wewnętrznych, które nie są świadome potrzebnych typów danych.
Kiedy robisz
df.loc[0]
to konwertujepd.Series
,A teraz
Series
będzie miał tylko jedendtype
. W ten sposób zmuszającint
dofloat
.Zamiast tego zachowaj strukturę jako
pd.DataFrame
,Wybierz wiersz potrzebny jako ramka, a następnie przekonwertuj na
dict
Podobnie, aby dodać nowy wiersz, użyj
pd.DataFrame.append
funkcji pandy ,Powyższe nie spowoduje konwersji typu,
źródło
Inne podejście z niewielkimi manipulacjami danymi:
Załóżmy, że masz listę słowników (lub ramek danych)
lod=[{'a': [1], 'b': [2.2]}, {'a': [5], 'b': [4.4]}]
gdzie każdy słownik reprezentuje wiersz (zwróć uwagę na listy w drugim słowniku). Następnie możesz łatwo utworzyć ramkę danych za pomocą:
i zachowujesz typy kolumn. Zobacz konkat
Więc jeśli masz ramkę danych i listę nagrań, możesz po prostu użyć
źródło
W pierwszym przypadku możesz pracować z typem danych o wartości całkowitej zerowej . Wybór serii nie jest wymagany,
float
a wartości są umieszczane wobject
kontenerze. Słownik jest następnie poprawnie tworzony, a podstawowa wartość jest przechowywana jakonp.int64
.W przypadku twojej składni działa to prawie również w drugim przypadku, ale to upcasting do
object
, więc nie świetne:Możemy jednak wprowadzić niewielką zmianę w składni, aby dodać wiersz na końcu (z RangeIndex), a teraz typy są obsługiwane poprawnie.
źródło