Искам да преобразувам таблица, представена като списък от списъци, в Pandas DataFrame. Като изключително опростен пример:
a = [['a', '1.2', '4.2'], ['b', '70', '0.03'], ['x', '5', '0']]
df = pd.DataFrame(a)
Какъв е най-добрият начин да преобразувам колоните в подходящи типове, в този случай колони 2 и 3 в floats? Има ли начин да се посочат типовете при конвертирането в DataFrame? Или е по-добре първо да създадем DataFrame и след това да преминем през колоните, за да променим типа за всяка колона? В идеалния случай бих искал да направя това по динамичен начин, защото може да има стотици колони и не искам да посочвам точно кои колони от какъв тип са. Единственото, което мога да гарантирам, е, че всяка колона съдържа стойности от един и същи тип.
Имате три основни възможности за конвертиране на типове в pandas:
to_numeric()
- предоставя функционалност за безопасно преобразуване на нецифрови типове (например низове) в подходящ цифров тип. (Вижте също to_datetime()
и to_timedelta()
.)astype()
- преобразува (почти) всеки тип в (почти) всеки друг тип (дори ако не е задължително да се прави това). Позволява също така да конвертирате в categorial типове (много полезно).infer_objects()
- полезен метод за конвертиране на обектни колони, съдържащи обекти от Python, в тип на pandas, ако това е възможно.
Прочетете по-нататък за по-подробни обяснения и употреба на всеки от тези методи.to_numeric()
Най-добрият начин да конвертирате една или повече колони на DataFrame в числови стойности е да използвате pandas.to_numeric()
.
Тази функция ще се опита да промени нецифрови обекти (като низове) в цели числа или числа с плаваща запетая, както е подходящо.
Входът за to_numeric()
е серия или една колона от DataFrame.
>>> s = pd.Series(["8", 6, "7.5", 3, "0.9"]) # mixed string and numeric values
>>> s
0 8
1 6
2 7.5
3 3
4 0.9
dtype: object
>>> pd.to_numeric(s) # convert everything to float values
0 8.0
1 6.0
2 7.5
3 3.0
4 0.9
dtype: float64
Както можете да видите, връща се нова серия. Не забравяйте да присвоите този изход към променлива или име на колона, за да продължите да го използвате:
# convert Series
my_series = pd.to_numeric(my_series)
# convert column "a" of a DataFrame
df["a"] = pd.to_numeric(df["a"])
Можете също така да го използвате за преобразуване на няколко колони от DataFrame чрез метода apply()
:
# convert all columns of DataFrame
df = df.apply(pd.to_numeric) # convert all columns of DataFrame
# convert just columns "a" and "b"
df[["a", "b"]] = df[["a", "b"]].apply(pd.to_numeric)
Ако всички ваши стойности могат да бъдат конвертирани, това вероятно е всичко, от което се нуждаете.
Но какво ще стане, ако някои стойности не могат да бъдат конвертирани в числов тип?
Функцията to_numeric()
приема и аргумент с ключова дума errors
, който ви позволява да принудите нецифровите стойности да бъдат NaN
или просто да игнорирате колони, съдържащи такива стойности.
Ето един пример, в който се използва поредица от низове s
, която има обектния тип d:
>>> s = pd.Series(['1', '2', '4.7', 'pandas', '10'])
>>> s
0 1
1 2
2 4.7
3 pandas
4 10
dtype: object
Поведението по подразбиране е да повдигне въпроса, ако не може да преобразува стойност. В този случай тя не може да се справи с низа 'pandas':
>>> pd.to_numeric(s) # or pd.to_numeric(s, errors='raise')
ValueError: Unable to parse string
Вместо да се проваля, може да искаме 'pandas' да се счита за липсваща/лоша числова стойност. Можем да накараме невалидните стойности да се превърнат в NaN
по следния начин, като използваме аргумента на ключовата дума errors
:
>>> pd.to_numeric(s, errors='coerce')
0 1.0
1 2.0
2 4.7
3 NaN
4 10.0
dtype: float64
Третият вариант за errors
е просто да игнорираме операцията, ако се срещне невалидна стойност:
>>> pd.to_numeric(s, errors='ignore')
# the original Series is returned untouched
Последната опция е особено полезна, когато искате да конвертирате цялата си DataFrame, но не знаете кои от колоните ни могат да бъдат конвертирани надеждно в числов тип. В този случай просто напишете:
df.apply(pd.to_numeric, errors='ignore')
Функцията ще бъде приложена към всяка колона на DataFrame. Колоните, които могат да бъдат конвертирани в числов тип, ще бъдат конвертирани, докато колоните, които не могат (например съдържат нецифрени низове или дати), ще бъдат оставени настрана.
По подразбиране преобразуването с to_numeric()
ще ви даде или int64
, или float64
dtype (или каквато е ширината на цялото число, характерна за вашата платформа).
Обикновено това е, което искате, но какво ще стане, ако искате да спестите малко памет и да използвате по-компактен dtype, като float32
или int8
?
Функцията to_numeric()
ви дава възможност да се превърнете в 'integer', 'signed', 'unsigned', 'float'. Ето един пример за проста серия s
от целочислен тип:
>>> s = pd.Series([1, 2, -7])
>>> s
0 1
1 2
2 -7
dtype: int64
Прехвърлянето към 'integer' използва най-малкото възможно цяло число, което може да побере стойностите:
>>> pd.to_numeric(s, downcast='integer')
0 1
1 2
2 -7
dtype: int8
При даункастинг към 'float' по същия начин се избира по-малък от нормалния тип float:
>>> pd.to_numeric(s, downcast='float')
0 1.0
1 2.0
2 -7.0
dtype: float32
astype()
Методът astype()
ви дава възможност да посочите изрично типа d, който искате да има вашата DataFrame или Series. Той е много универсален, тъй като можете да опитате да преминете от един тип към друг.
Просто изберете тип: можете да използвате NumPy dtype (напр. np.int16
), някои Python типове (напр. bool) или специфични за pandas типове (като categorical dtype).
Извикайте метода върху обекта, който искате да конвертирате, и astype()
ще се опита да го конвертира вместо вас:
# convert all DataFrame columns to the int64 dtype
df = df.astype(int)
# convert column "a" to int64 dtype and "b" to complex type
df = df.astype({"a": int, "b": complex})
# convert Series to float16 type
s = s.astype(np.float16)
# convert Series to Python strings
s = s.astype(str)
# convert Series to categorical type - see docs for more details
s = s.astype('category')
Обърнете внимание, че казах "опитай" - ако astype()
не знае как да преобразува стойност в Series или DataFrame, ще обяви грешка. Например, ако имате стойност NaN
или inf
, ще получите грешка при опит да я преобразувате в цяло число.
От версия 0.20.0 на pandas тази грешка може да се потисне, като се подаде errors='ignore'
. Оригиналният ви обект ще бъде върнат непокътнат.
Функцията astype()
е мощна, но понякога преобразува стойности "неправилно". Например:
>>> s = pd.Series([1, 2, -7])
>>> s
0 1
1 2
2 -7
dtype: int64
Това са малки цели числа, така че какво ще кажете за конвертиране в беззнаков 8-битов тип, за да спестите памет?
>>> s.astype(np.uint8)
0 1
1 2
2 249
dtype: uint8
pd.to_numeric(s, downcast='unsigned')
вместо това може да помогне за предотвратяване на тази грешка.infer_objects()
Във версия 0.21.0 на pandas е въведен методът infer_objects()
за преобразуване на колони от DataFrame, които имат тип данни обект, в по-специфичен тип (меки преобразувания).
Например, ето една DataFrame с две колони от тип обект. Едната съдържа реални цели числа, а другата - низове, представляващи цели числа:
>>> df = pd.DataFrame({'a': [7, 1, 5], 'b': ['3','2','1']}, dtype='object')
>>> df.dtypes
a object
b object
dtype: object
Използвайки infer_objects()
, можете да промените типа на колоната 'a' на int64:
>>> df = df.infer_objects()
>>> df.dtypes
a int64
b object
dtype: object
Колона 'b' е оставена сама, тъй като нейните стойности са низове, а не цели числа. Ако искате да се опитате да преобразувате и двете колони в тип цяло число, можете да използвате df.astype(int)
вместо това.
Какво ще кажете за това?
a = [['a', '1.2', '4.2'], ['b', '70', '0.03'], ['x', '5', '0']]
df = pd.DataFrame(a, columns=['one', 'two', 'three'])
df
Out[16]:
one two three
0 a 1.2 4.2
1 b 70 0.03
2 x 5 0
df.dtypes
Out[17]:
one object
two object
three object
df[['two', 'three']] = df[['two', 'three']].astype(float)
df.dtypes
Out[19]:
one object
two float64
three float64
Ето една функция, която приема като аргументи DataFrame и списък с колони и превръща всички данни в колоните в числа.
# df is the DataFrame, and column_list is a list of columns as strings (e.g ["col1","col2","col3"])
# dependencies: pandas
def coerce_df_columns_to_numeric(df, column_list):
df[column_list] = df[column_list].apply(pd.to_numeric, errors='coerce')
И така, за вашия пример:
import pandas as pd
def coerce_df_columns_to_numeric(df, column_list):
df[column_list] = df[column_list].apply(pd.to_numeric, errors='coerce')
a = [['a', '1.2', '4.2'], ['b', '70', '0.03'], ['x', '5', '0']]
df = pd.DataFrame(a, columns=['col1','col2','col3'])
coerce_df_columns_to_numeric(df, ['col2','col3'])