Змінення типу даних стовпців у Pandas


804

Я хочу перетворити таблицю, представлену як список списків, в Pandas DataFrame. Як надзвичайно спрощений приклад:

a = [['a', '1.2', '4.2'], ['b', '70', '0.03'], ['x', '5', '0']]
df = pd.DataFrame(a)

Який найкращий спосіб перетворити стовпці у відповідні типи, у цьому випадку стовпці 2 та 3 у плавки? Чи є спосіб вказати типи під час переходу до DataFrame? Або краще спершу створити DataFrame, а потім прокрутити колонки, щоб змінити тип для кожного стовпця? В ідеалі я хотів би це зробити динамічним способом, тому що стовпців може бути сотні, і я не хочу точно вказувати, які саме колонки мають тип. Я можу гарантувати лише те, що кожен стовпчик містить значення одного типу.


Я бачив підходи до перетворення кожного стовпця та підходи до перетворення стовпців, названих спеціально, але як щодо певних стовпців, які відповідають певній умові, коли ви не можете перерахувати 100 стовпців, які ви хочете перетворити одразу? Я думаю, наприклад, про всі float64 -> float32 або інші тактики збереження пам'яті.
демонголем

@demongolem: ви можете зробити щось на кшталт df.apply(pd.to_numeric, downcast="integer", errors="ignore")заниження цілих стовпців до найменшого (цілого) типу, який буде містити значення.
Алекс Райлі

Відповіді:


1190

У вас є три основні варіанти перетворення типів у пандах:

  1. to_numeric()- забезпечує функціональність для безпечного перетворення нечислових типів (наприклад, рядків) у відповідний числовий тип. (Див. Також to_datetime()і to_timedelta().)

  2. astype()- перетворити (майже) будь-який тип у (майже) будь-який інший тип (навіть якщо це не обов'язково розумно робити). Також дозволяє перетворити на категоріальні типи (дуже корисно).

  3. infer_objects() - корисний метод для перетворення стовпців об'єктів, що містять об’єкти Python, у тип панди, якщо це можливо.

Читайте далі для більш детальних пояснень та використання кожного з цих методів.


1. 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що має тип об'єкта:

>>> 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

Замість того, щоб вийти з ладу, ми можемо хотіти, щоб «панди» вважалися відсутнім / неправильним числовим значенням. Ми можемо примусити недійсні значення 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()дасть вам або a, int64або float64dtype (або будь-яка ціла ширина є власною для вашої платформи).

Зазвичай це те, що ви хочете, але що робити, якщо ви хочете зберегти трохи пам’яті та використати більш компактний тип, наприклад float32, або int8?

to_numeric()надає вам можливість перейти до "цілого", "підписаного", "непідписаного", "плавати". Ось приклад для простої серії sцілого типу:

>>> s = pd.Series([1, 2, -7])
>>> s
0    1
1    2
2   -7
dtype: int64

Перехід на "ціле число" використовує найменше ціле число, яке може містити значення:

>>> pd.to_numeric(s, downcast='integer')
0    1
1    2
2   -7
dtype: int8

Downcasting на "float" аналогічно вибирає менший, ніж звичайний, плаваючий тип:

>>> pd.to_numeric(s, downcast='float')
0    1.0
1    2.0
2   -7.0
dtype: float32

2. astype()

astype()Метод дозволяє бути чітко про DTYPE ви хочете , щоб ваш DataFrame або серії , щоб мати. Це дуже універсально, тому що ви можете спробувати перейти від одного типу до іншого.

Основне використання

Просто виберіть тип: ви можете використовувати тип NumPy (наприклад np.int16), деякі типи Python (наприклад, bool) або типи, пов’язані з пандами (наприклад, категоричний тип).

Викличте метод на об'єкті, який ви хочете перетворити, і 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, цю помилку можна придушити шляхом проходження 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

Перетворення працювало, але -7 було завернуто круглим, щоб стати 249 (тобто 2 8 - 7)!

Спроба знищити її за допомогою pd.to_numeric(s, downcast='unsigned')може допомогти запобігти цій помилці.


3. infer_objects()

У версії 0.21.0 панд введено метод 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)натомість.


8
Також, на відміну від .astype (float), це перетворить рядки в NaNs замість того, щоб викликати помилку
Роб

11
.convert_objectsзнешкоджений, оскільки 0.17- використовуйте df.to_numericзамість цього
Матті Ліра

4
Дякую - я мушу оновити цю відповідь. Можливо, варто відзначити, що pd.to_numericі його супутні методи працюватимуть лише в одному стовпчику одночасно, на відміну від цього convert_objects. Дискусія щодо функції заміни в API, здається, триває ; Я сподіваюся, що метод, який працює в цілому DataFrame, залишиться, тому що це дуже корисно.
Алекс Райлі

Який найкращий спосіб конвертувати всі стовпці, які зараз є, скажімо, int64в int32?
RoyalTS

4
@RoyalTS: напевно, найкраще використовувати astype(як в іншій відповіді), тобто .astype(numpy.int32).
Алекс Райлі

447

Як щодо цього?

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

10
Так! pd.DataFrameмає dtypeаргумент, який може дозволяти вам робити те, що ви шукаєте. df = pd.DataFrame (a, колонки = ['один', 'два', 'три'], dtype = float) В [2]: df.dtypes Out [2]: один об'єкт два float64 три float64 dtype: object
іменаbarbara

17
Коли я намагаюся, як пропонується, отримую попередження SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_index,col_indexer] = value instead. Це можливо було введено в новій версії панди, і я не бачу нічого поганого в результаті, але мені просто цікаво, про що йдеться в цьому попередженні. Будь-яка ідея?
помаранчевий

2
@orange попередження полягає в тому, щоб попередити користувачів про потенційно заплутану поведінку з ланцюговими операціями та пандами, що повертають копії, а не редагування фреймів даних. див. stackoverflow.com/questions/20625582/… та пов'язані з ними.
А.Ван

19
Це хороший метод, але він не працює, коли в колонці є NaN. Поняття не маю, чому NaN просто не може залишатись NaN, коли передає флот до int:ValueError: Cannot convert NA to integer
Віталій Ісаєв,

7
@GillBates так, у словнику. df = pd.DataFrame(a, columns=['one', 'two', 'three'], dtype={'one': str, 'two': int, 'three': float}). Мені важко знайти специфікацію для прийнятих значень "dtype". Список був би непоганим (зараз я це роблю dict(enumerate(my_list))).
FichteFoll

39

цей нижче код змінить тип даних стовпця.

df[['col.name1', 'col.name2'...]] = df[['col.name1', 'col.name2'..]].astype('data_type')

замість типу даних ви можете надати свій тип даних. що ви хочете, як str, float, int і т.д.


Зауважте, що застосовуючи це до стовпця, що містить рядки `` 'True' '`` `та` `` False' `` `за допомогою data_type bool, все змінюється на True.
Х. Вабрі

Цей параметр також можна перетворити на тип "категорія"
neves

17

Коли мені потрібно було лише вказати конкретні стовпці, і я хочу бути явним, я використовував (за DOCS LOCATION ):

dataframe = dataframe.astype({'col_name_1':'int','col_name_2':'float64', etc. ...})

Отже, використовуючи оригінальне запитання, але надаючи йому назви стовпців ...

a = [['a', '1.2', '4.2'], ['b', '70', '0.03'], ['x', '5', '0']]
df = pd.DataFrame(a, columns=['col_name_1', 'col_name_2', 'col_name_3'])
df = df.astype({'col_name_2':'float64', 'col_name_3':'float64'})

15

Ось функція, яка приймає в якості аргументів 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'])

що робити, якщо ви хочете використовувати індекси стовпців замість імен стовпців?
jvalenti

8

Як щодо створення двох фреймів даних, кожен з яких має різні типи даних для своїх стовпців, а потім додавання їх разом?

d1 = pd.DataFrame(columns=[ 'float_column' ], dtype=float)
d1 = d1.append(pd.DataFrame(columns=[ 'string_column' ], dtype=str))

Результати

In[8}:  d1.dtypes
Out[8]: 
float_column     float64
string_column     object
dtype: object

Після створення фрейму даних ви можете заповнити його змінними з плаваючою комою в 1-му стовпці, а рядки (або будь-який тип даних, який ви бажаєте) у 2-му стовпці.


4

панди> = 1,0

Ось діаграма, яка резюмує деякі найважливіші конверсії в пандах.

введіть тут опис зображення

Перетворення на рядок є тривіальними .astype(str)і не показані на малюнку.

"Жорсткі" та "м'які" конверсії

Зауважте, що "перетворення" в цьому контексті можуть або позначати перетворення текстових даних у їх фактичний тип даних (жорстке перетворення), або виводити більш відповідні типи даних для даних у стовпцях об'єктів (м'яке перетворення). Щоб проілюструвати різницю, погляньте

df = pd.DataFrame({'a': ['1', '2', '3'], 'b': [4, 5, 6]}, dtype=object)
df.dtypes                                                                  

a    object
b    object
dtype: object

# Actually converts string to numeric - hard conversion
df.apply(pd.to_numeric).dtypes                                             

a    int64
b    int64
dtype: object

# Infers better data types for object data - soft conversion
df.infer_objects().dtypes                                                  

a    object  # no change
b     int64
dtype: object

# Same as infer_objects, but converts to equivalent ExtensionType
df.convert_dtypes().dtypes                                                     

1

Я думав, що у мене така ж проблема, але насправді у мене є незначна різниця, яка полегшує вирішення проблеми. Для інших, хто дивиться на це питання, варто перевірити формат списку вводу. У моєму випадку числа спочатку плавають не рядками, як у питанні:

a = [['a', 1.2, 4.2], ['b', 70, 0.03], ['x', 5, 0]]

але надто обробляючи список перед створенням фрейму даних, я втрачаю типи і все стає рядком.

Створення фрейму даних за допомогою масиву numpy

df = pd.DataFrame(np.array(a))

df
Out[5]: 
   0    1     2
0  a  1.2   4.2
1  b   70  0.03
2  x    5     0

df[1].dtype
Out[7]: dtype('O')

дає той самий кадр даних, як у запитанні, де записи у стовпцях 1 і 2 розглядаються як рядки. Однак робити

df = pd.DataFrame(a)

df
Out[10]: 
   0     1     2
0  a   1.2  4.20
1  b  70.0  0.03
2  x   5.0  0.00

df[1].dtype
Out[11]: dtype('float64')

насправді надає кадр даних зі стовпцями у правильному форматі


0

Починаючи з панд 1.0.0, у нас є pandas.DataFrame.convert_dtypes. Ви навіть можете контролювати, які типи конвертувати!

In [40]: df = pd.DataFrame(
    ...:     {
    ...:         "a": pd.Series([1, 2, 3], dtype=np.dtype("int32")),
    ...:         "b": pd.Series(["x", "y", "z"], dtype=np.dtype("O")),
    ...:         "c": pd.Series([True, False, np.nan], dtype=np.dtype("O")),
    ...:         "d": pd.Series(["h", "i", np.nan], dtype=np.dtype("O")),
    ...:         "e": pd.Series([10, np.nan, 20], dtype=np.dtype("float")),
    ...:         "f": pd.Series([np.nan, 100.5, 200], dtype=np.dtype("float")),
    ...:     }
    ...: )

In [41]: dff = df.copy()

In [42]: df 
Out[42]: 
   a  b      c    d     e      f
0  1  x   True    h  10.0    NaN
1  2  y  False    i   NaN  100.5
2  3  z    NaN  NaN  20.0  200.0

In [43]: df.dtypes
Out[43]: 
a      int32
b     object
c     object
d     object
e    float64
f    float64
dtype: object

In [44]: df = df.convert_dtypes()

In [45]: df.dtypes
Out[45]: 
a      Int32
b     string
c    boolean
d     string
e      Int64
f    float64
dtype: object

In [46]: dff = dff.convert_dtypes(convert_boolean = False)

In [47]: dff.dtypes
Out[47]: 
a      Int32
b     string
c     object
d     string
e      Int64
f    float64
dtype: object
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.