Pandas: API: .convert_objects устарел, хотим ли мы заменить .convert?

Созданный на 2 окт. 2015  ·  46Комментарии  ·  Источник: pandas-dev/pandas

xref # 11173

или ИМХО просто замените на pd.to_datetime,pd.to_timedelta,pd.to_numeric .

Иметь автоугадателя - это нормально, но когда вы пытаетесь принудить, все может пойти наперекосяк.

API Design Compat Needs Discussion

Самый полезный комментарий

Да здравствует convert_objects!

Все 46 Комментарий

cc @bashtage
@jorisvandenbossche @shoyer @TomAugspurger @sinhrks

Уже есть _convert, которого можно было бы продвигать.

Пт, 2 октября 2015 г., 10:16 Джефф Рэйбэк (Jeff Reback) [email protected] написал:

cc @bashtage https://github.com/bashtage
@jorisvandenbossche https://github.com/jorisvandenbossche @shoyer
https://github.com/shoyer @TomAugspurger
https://github.com/TomAugspurger @sinhrks https://github.com/sinhrks

-
Ответьте на это письмо напрямую или просмотрите его на GitHub
https://github.com/pydata/pandas/issues/11221#issuecomment -145033282.

Преимущество хорошо разработанного convert том, что он работает с DataFrames. Все to_* предназначены только для одномерных типов.

@bashtage о, я согласен.

Проблема в том, что coerce , вы должны в основном частично не приводить к автоматическому принуждению и поэтому оставлять неоднозначные вещи на усмотрение пользователя (через 1-мерное использование pd.to_* ). Но если предположить, что мы это сделаем, тогда да, вы можете заставить это работать.

Я просто подумал о случае, когда я импортировал данные, которые должны быть числовыми, в DF, но в нем есть несколько смешанных символов, и мне нужны только числа или NaN. Этот тип преобразования - то, что я в конечном итоге хотел, когда я начал смотреть на convert_objects когда я был удивлен, что запрос coerce всех строк не привел к NaN.

но проблема в том, что смешанное логическое / наноразмерное значение неоднозначно (так что, возможно, просто нужно `` обработать '' это)

Некоторые комментарии / наблюдения:

  • На самом деле мне нравится convert_objects больше, чем convert , потому что он более четко говорит о том, что он делает: попробуйте преобразовать столбцы с типами объектов во встроенный dtype (convert является довольно общим).
  • Если мы решим, что у нас есть что-то вроде текущего _функциональности_ преобразованных объектов, я действительно не вижу причин отказываться от convert_objects для нового converts . Я думаю, что технически возможно отказаться от старых _keywords_ (а не от функции) в пользу новых ключевых слов. (на самом деле оригинальный подход в обратном PR).
  • Я думаю, что функциональность convert_objects полезна (как уже было сказано выше: вы можете делать что-то вроде to_datetime/to_numeric/.. на фреймах данных). Использование функций to_.. для каждой серии по отдельности всегда будет предпочтительным решением для надежного кода, но до тех пор, пока convert_objects очень четко определено (теперь есть некоторые странные несоответствия), я думаю, что это так. полезно иметь это. Было бы очень хорошо, если бы это можно было просто реализовать в терминах методов to_.. .
    Немного упрощен псевдокод:

def convert_objects(self, numeric=False, datetime=False, timedelta=False, coerce=False): for each column: if numeric: pd.to_numeric(self, coerce=coerce) elif datetime: pd.to_datetime(self, coerce=coerce) elif timedelta: pd.to_timedelta(self, coerce=coerce)

  • Но основная проблема заключается в следующем: причина, по convert_objects сейчас полезна, заключается именно в том, что у него есть дополнительное «правило», которого нет в методах to_.. : _only преобразовать столбец, если есть по крайней мере одно значение, которое можно преобразовать_.
    Это причина того, что работает что-то вроде этого:

`` ''
В [2]: df = pd.DataFrame ({'int_str': ['1', '2'], 'real_str': ['a', 'b']})

В [3]: df.convert_objects (convert_numeric = True)
Из [3]:
int_str real_str
0 1 а
1 2 б

В [4]: ​​df.convert_objects (convert_numeric = True) .dtypes
Из [4]:
int_str int64
объект real_str
dtype: объект
`` ''

и не дает:

Out[3]: int_str real_str 0 1 NaN 1 2 NaN

что было бы бесполезно (хотя, возможно, более предсказуемо). Тот факт, что не всегда принуждение к NaN, считался ошибкой, для которой @bashtage сделал PR (а для to_numeric также логично, что он возвращает NaN). Но это сделало convert_objects менее полезным (так что в конце концов его отменили).
Поэтому я думаю, что в этом случае нам придется отклониться от поведения to_..

Возможно, это может быть дополнительным параметром для convert/convert_objects : нужно ли приводить неконвертируемые столбцы к NaN или нет (что означает: столбцы, для которых нет хотя бы одного конвертируемого элемента и приведет к полному столбцу NaN) . @bashtage, тогда вы можете иметь

Хорошо, вопрос в том, должны ли мы отказаться от convert_objects thrn?

Я на самом деле думаю, что convert - это гораздо лучшее название, и мы, безусловно, могли бы добавить параметры, которые вы описываете, чтобы сделать его более полезным

convert_objects просто кажется плохой функцией API, поскольку у него есть зависимость от пути, где он

пытается преобразовать в тип
пытается преобразовать в тип b в случае неудачи, но не в случае успеха
пытается преобразовать в тип c - a и b терпят неудачу, но не в случае успеха

В лучшем дизайне будет преобразован только один тип, что устранит любую двусмысленность, если некоторые данные когда-либо будут преобразованы в более чем один тип. to to_* вроде как добираются туда, с оговоркой, что они работают столбец за столбцом.

Да здравствует convert_objects!

может быть, нам нужно в документации несколько примеров, показывающих:

df.apply(pd.to_numeric) и тому подобное (что эффективно / безопаснее) заменяет .convert_objects

Всем привет,

В настоящее время я использую convert_objects во многих своих кодах, и я думаю, что эта функция очень полезна при импорте наборов данных, которые могут каждый день различаться с точки зрения состава столбцов. Действительно ли необходимо отказаться от него или есть шанс сохранить его?

Большое спасибо,
Умберто

.convert_objects было неоднозначным по своей сути, и несколько версий назад оно устарело. см. документацию здесь, чтобы узнать, как явно выполнить преобразование объекта.

Я согласен с @jreback - convert_objects был полон магии, и ему было трудно угадать поведение, которое было несовместимым для разных целей конверсии (например, числа не были принудительными, если все не были числами, даже если было сказано принудить).

Хорошо спроектированный отгадыватель с ясным, простым правилом и без возможности принуждения может быть полезен, но нетрудно написать свое собственное с вашим любимым набором правил.

К вашему сведению, параметры convert all (errors = 'coerce') и ignore (errors = 'ignore') в .to_numeric являются проблемой в файлах данных, содержащих столбцы строк, которые вы хотите сохранить, и столбцы строк, которые на самом деле являются числами, выраженными в научная нотация (например, 6.2e + 15), которая требует «принуждения» для преобразования из строк в float64.

В (устаревшем) файле convert.py есть удобная функция мягкого преобразования, которая проверяет, производит ли принудительное преобразование все NaN (например, строку, которую вы хотите сохранить), а затем отклоняет преобразование всего столбца.

Четвертый вариант ошибки, такой как «мягкое принуждение», будет улавливать числа в научной нотации, не переводя все строки в NaN.

На данный момент моя работа:

    for col in df.columns:   
        converted = pd.to_numeric(df[col],errors='coerce')  
        df[col] = converted if not pd.isnull(converted).all() else df[col]

Преимущество convert_objects перед различными методами to_* заключается в том, что вам не нужно заранее знать типы данных. Как сказал @usagliaschi , у вас могут быть разнородные данные, и вам нужна одна функция для их обработки. Это как раз моя нынешняя ситуация.

Есть ли какая-либо замена функции, которая будет соответствовать этой функциональности, в частности, вывести даты / время?

xref https://github.com/pandas-dev/pandas/pull/15757#issuecomment -288090118

Я думаю, что стоило бы раскрыть то, что новый API-интерфейс soft convert 0.20 (я не рассматривал его подробно), сославшись на него в сообщении depr convert_objects, а затем, если возможно, отложить convert_objects до следующей версии.

Я говорю это, потому что знаю, что есть люди (например, я), которые проигнорировали сообщение convert_objects depr в нескольких случаях, в частности, работая с данными, где вы не обязательно знаете столбцы. Реальный экземпляр:

df = pd.read_html(source)[0]  # poorly formatted table, everything inferred to object
                              # exact columns can vary

df.columns = df.loc[0, :]
df = df.drop(0).dropna()

df = df.convert_objects()

Посмотрев на это еще раз, я понимаю, что df.apply(lambda x: pd.to_numeric(x, errors='ignore')) также будет работать нормально в этом случае, но это было не сразу очевидно, и я не уверен, что мы достаточно держались за руки (из-за отсутствия лучшего термина), чтобы помочь людям перейти.

ЕСЛИ мы решим предоставить «объекты мягкого преобразования», хотели бы мы, чтобы это называлось .covert_objects() ? или другое имя, может быть .convert() ? (например, вместо того, чтобы удалить устаревание, мы просто изменили его - что, вероятно, больше нарушает обратную совместимость).

xref # 15550

поэтому я думаю, что решение этого может быть:

  • добавление .to_* в Series (# 15550)
  • добавление .to_* к DataFrame
  • добавление опции soft

тогда достаточно просто сделать:

df.to_numeric(errors='soft')

если вы действительно действительно хотите преобразовать вещи в исходный .convert_object() .

df.to_datetime(errors='soft').to_timedelta(errors='soft').to_numeric(errors='soft')

И я полагаю, что мог бы предложить удобную функцию для этого:

  • df.to_converted()
  • df.convert() (возможно, слишком общий)
  • df.convert_objects() (воскресить)
  • df.to_just_figure_this_out()

Я думаю, что наиболее полезная функция мягкого преобразования могла бы иметь либо возможность упорядочить правила to_* , например, числовое-дата-время или время-дата-числовое, поскольку иногда есть данные, которые могут быть интерпретированы как несколько типов. По крайней мере, так было в случае с convert_objects . В качестве альтернативы можно было выбрать только подмножество фильтров, например, учитывать только числовую дату.

Я согласен, что расширение to_* для правильной работы с DataFrames было бы полезно.

Спасибо @jreback - мне нравится добавлять to_... в DataFrame api, хотя, возможно, стоит выделить варианты использования. Рассмотрим этот плохо сформированный фрейм:

df = pd.DataFrame({'num_objects': [1, 2, 3], 'num_str': ['1', '2', '3']}, dtype=object)

df
Out[2]: 
  num_objects num_str
0           1       1
1           2       2
2           3       3

df.dtypes
Out[3]: 
num_objects    object
num_str        object
dtype: object

Поведение по умолчанию convert_objects - это только переинтерпретировать целые числа Python в правильный тип int dtype, а не приводить строки. Это поведение, которое я бы очень скучал по убийству convert_objects , и подозреваю, что другие тоже могут.

df.convert_objects().dtypes
Out[4]: 
num_objects     int64
num_str        object
dtype: object

In [5]: df.apply(pd.to_numeric).dtypes
Out[5]: 
num_objects    int64
num_str        int64
dtype: object

Так стоит ли добавлять convert_pyobjects (... не люблю это имя) только в этом случае?
infer_python_types
convert_python_types
??

Я думаю, что достаточно просто добавить опцию soft к errors чтобы сделать именно это.

Будет ли кастовать pd.Series(['1', '2', '3']).to_numeric(errors='soft') ?

soft просто вернет [3] (как и coerce

Разница в [4] (в нем серия). Я думаю, soft вернет [5], а coerce вернет [4]

In [3]: pd.to_numeric(pd.Series(['1', '2', '3']), errors='coerce')
Out[3]: 
0    1
1    2
2    3
dtype: int64

In [4]: pd.to_numeric(pd.Series(['1', '2', 'foo']), errors='coerce')
Out[4]: 
0    1.0
1    2.0
2    NaN
dtype: float64

In [5]: pd.to_numeric(pd.Series(['1', '2', 'foo']), errors='ignore')
Out[5]: 
0      1
1      2
2    foo
dtype: object

Спасибо за примеры.

Я все еще думаю, что «только преобразование объектов Python без потерь в правильные типы данных» может быть лучше в качестве отдельной операции от to_numeric ? Не было бы способа получить Out[4] из моего примера выше?

Я не думаю, что возможно преобразование объектов python без потерь в правильные типы dtypes, как правило, хорошо определено. Конечно, есть объекты, которые не имеют собственного представления без потерь (например, str-> float).

Эта только что описанная двусмысленность как раз и является проблемой при написании полезного, правильного и точного конвертера.

Следует ли описывать набор параметров преобразования и правила, которые будут использоваться, до их внедрения? Я думаю, они должны, иначе код по умолчанию будет эталонным набором правил (что было одной из проблем с convert_objects ).

Чтобы было ясно, то, что я имею в виду под преобразованием без потерь, делает именно то, что сделал бы pd.Series([<python objects]>) - преобразование в numpy dtype, если это возможно, в противном случае оставление как объект.

Я думаю, что смысл convert_objects и любого его преемника в том, чтобы строго выходить за рамки того, что эти инструменты io будут делать автоматически. IOW, некоторые принуждение некоторых объектов некоторое время имеет важное значение. Старый convert_objects , например, приводил бы смешанные строки и числа к числам и нулям. Такие инструменты, как read_csv намеренно не делают этого, поскольку это довольно произвольно.

to_* довольно точны и делают то, что вы им говорите, даже с не-объектами. Например:

import pandas as pd
import datetime as dt
t = pd.Series([dt.datetime.now(), dt.datetime.now()])

pd.to_numeric(t)
Out[7]: 
0    1490739351272159000
1    1490739351272159000
dtype: int64

Я бы предположил, что преемник convert_objects преобразует только object dtype и не будет вести себя подобным образом.

Причина, по которой мне не нравится добавлять функции .to_ качестве метода в DataFrame (или, по крайней мере, не в качестве решения в этом обсуждении), заключается в том, что IMO вы обычно не хотите применять это ко всем столбцам и / или не таким же образом (и если вы этого хотите, вы можете легко использовать подход apply как вы можете сделать сейчас).
Например, с DataFrame.to_datetime , я ожидал, что он сделает это для всех столбцов, что означает преобразование числовых столбцов в строковые столбцы. Я не думаю, что обычно это то, что вам нужно.

Поэтому для меня одна из причин использовать метод convert_objects (независимо от точных деталей поведения) заключается в том, что он будет пытаться преобразовать только фактические столбцы с типом object dtyped.

хорошо, если мы воскресим это с новой подписью. это актуально.

In [1]: DataFrame.convert_objects?
Signature: DataFrame.convert_objects(self, convert_dates=True, convert_numeric=False, convert_timedeltas=True, copy=True)
Docstring:
Deprecated.

Attempt to infer better dtype for object columns

Parameters
----------
convert_dates : boolean, default True
    If True, convert to date where possible. If 'coerce', force
    conversion, with unconvertible values becoming NaT.
convert_numeric : boolean, default False
    If True, attempt to coerce to numbers (including strings), with
    unconvertible values becoming NaN.
convert_timedeltas : boolean, default True
    If True, convert to timedelta where possible. If 'coerce', force
    conversion, with unconvertible values becoming NaT.
copy : boolean, default True
    If True, return a copy even if no copy is necessary (e.g. no
    conversion was done). Note: This is meant for internal use, and
    should not be confused with inplace.

IIRC @jorisvandenbossche предложил. (с модом).

DataFrame.convert_object(self, datetime=True, timedelta=True, numeric=False, copy=True)

Хотя если все поменять. Тогда, возможно, нам стоит просто переименовать это. (обратите внимание на .convert_object )

Извините, я просто возвращаюсь к этому, вот предложение о том, как я думаю, это могло бы работать, открытое для предложений по любой части.

0.20.1 - оставьте convert_objects но обновите сообщение depr новыми методами, которые я буду использовать
0.20.2 - удалить convert_objects

Во-первых, для преобразований, которые представляют собой просто распаковку объектов Python, добавьте новый метод infer_objects без параметров. Это по существу повторно применяет наш вывод ctor к любым столбцам объекта, и если столбец может быть без потерь распакован в собственный тип, сделайте это, в противном случае оставьте без изменений. Полезно в случайных сценариях, в которых исходный вывод терпит неудачу. Пример:

df = pd.DataFrame({'a': ['a', 1, 2, 3],
                   'b': ['b', 2.0, 3.0, 4.1],
                   'c': ['c', datetime.datetime(2016, 1, 1), datetime.datetime(2016, 1, 2), 
                         datetime.datetime(2016, 1, 3)]})

df = df.iloc[1:]

In [194]: df
Out[194]: 
   a    b                    c
1  1    2  2016-01-01 00:00:00
2  2    3  2016-01-02 00:00:00
3  3  4.1  2016-01-03 00:00:00

In [195]: df.dtypes
Out[195]: 
a    object
b    object
c    object
dtype: object

# exactly what convert_objects does in this scenario today!
In [196]: df.infer_objects().dtypes
Out[196]: 
a             int64
b           float64
c    datetime64[ns]
dtype: object

Во-вторых, для всех других преобразований добавьте to_numeric , to_datetime и to_datetime в DataFrame API со следующей подписью. В основном работают так же, как и сегодня, но есть некоторые варианты выбора удобных столбцов. Не уверен в настройках по умолчанию, начиная с наиболее "удобных".

"""
DataFrame.to_...(self, errors='ignore', object_only=True, include=None, exclude=None)
Parameters
------------
errors: {'ignore', 'coerce', 'raise'}
   error mode passed to `pd.to_....`
object_only: boolean
    if True, only apply inference to object typed columns

include / exclude: column selection
"""

Пример кадра, с тем, что нужно сегодня:

df1 = pd.DataFrame({
    'date': pd.date_range('2014-01-01', periods=3),
    'date_unconverted': ['2014-01', '2015-01', '2016-01'],
    'number': [1, 2, 3],
    'number_unconverted': ['1', '2', '3']})


In [198]: df1
Out[198]: 
        date date_unconverted  number number_unconverted
0 2014-01-01          2014-01       1                  1
1 2014-01-02          2015-01       2                  2
2 2014-01-03          2016-01       3                  3

In [199]: df1.dtypes
Out[199]: 
date                  datetime64[ns]
date_unconverted              object
number                         int64
number_unconverted            object
dtype: object


In [202]: df1.convert_objects(convert_numeric=True, convert_dates='coerce').dtypes
C:\Users\chris.bartak\AppData\Local\Continuum\Anaconda3\lib\site-packages\ipykernel_launcher.py:1: FutureWarning: convert_objects is deprecated.  Use the data-type specific converters pd.to_datetime, pd.to_timedelta and pd.to_numeric.
  """Entry point for launching an IPython kernel.
Out[202]: 
date                  datetime64[ns]
date_unconverted      datetime64[ns]
number                         int64
number_unconverted             int64
dtype: object

С новым API:

In [202]: df1.to_numeric().to_datetime()
Out[202]: 
date                  datetime64[ns]
date_unconverted      datetime64[ns]
number                         int64
number_unconverted             int64
dtype: object

И, честно говоря, меня лично не волнует второй API, мой отказ от отказа от поддержки convert_objects был полностью основан на отсутствии чего-то вроде infer_objects

Я бы добавил infer_objects() если правила были кристально ясными и реализация соответствовала описанию. Другой важный вариант использования - это когда в конце получается DF, транспонированный со всеми столбцами объекта, а затем что-то вроде df = df.T.infer_types() дает

Я думаю, что такие функции, как to_numeric и т. Д., Не должны использовать методы в фрейме данных, а должны быть просто автономными. Я не думаю, что они используются достаточно часто, чтобы загрязнитьсписок.

Круто, да, чем больше я думаю, тем меньше думаю, что добавление to_... в API DataFrame - хорошая идея. С точки зрения infer_objects impl в основном будет следующим - на основе maybe_convert_objects , что, как правило, неудивительно (на мой взгляд):

In [251]: from pandas._libs.lib import maybe_convert_objects

In [252]: converter = lambda x: maybe_convert_objects(np.asarray(x, dtype='O'), convert_datetime=True, convert_timedelta=True)

In [253]: converter([1,2,3])
Out[253]: array([1, 2, 3], dtype=int64)

In [254]: converter([1,2,3])
Out[254]: array([1, 2, 3], dtype=int64)

In [255]: converter([1,2,'3'])
Out[255]: array([1, 2, '3'], dtype=object)

In [256]: converter([datetime.datetime(2015, 1, 1), datetime.datetime(2015, 1, 2)])
Out[256]: array(['2015-01-01T00:00:00.000000000', '2015-01-02T00:00:00.000000000'], dtype='datetime64[ns]')

In [257]: converter([datetime.datetime(2015, 1, 1), 'a'])
Out[257]: array([datetime.datetime(2015, 1, 1, 0, 0), 'a'], dtype=object)

In [258]: converter([datetime.datetime(2015, 1, 1), 1])
Out[258]: array([datetime.datetime(2015, 1, 1, 0, 0), 1], dtype=object)

In [259]: converter([datetime.timedelta(seconds=1), datetime.timedelta(seconds=1)])
Out[259]: array([1000000000, 1000000000], dtype='timedelta64[ns]')

In [260]: converter([datetime.timedelta(seconds=1), 1])
Out[260]: array([datetime.timedelta(0, 1), 1], dtype=object)

да, возможно ,_convert_objects - это мягкое преобразование
он будет преобразован только в том случае, если все они строго конвертируемы

В этом случае я мог бы быть на борту с очень простым .infer_objects() . Думаю, он не примет никаких аргументов?

можно добавить новую функцию и изменить сообщение об устаревании convert_objects, чтобы указать на .infer_objects() и .to_* на 0,21, а затем удалить в 1.0

@jreback : Судя по этому разговору, удаление convert_objects не произойдет в 0.21. Было бы лучше закрыть # 15757 и позволить новому PR занять свое место для реализации infer_objects (что, кстати, кажется хорошей идеей)?

IIUC, в какой степени infer_objects просто порт convert_objects чтобы быть методом DataFrame (или просто NDFrame в целом)?

convert_objects имеет собственную логику и варианты. infer_objects должен использовать вывод по умолчанию как если бы для DataFrame (но только столбцы объектов).

А, верно, значит, вы имеете в виду, что infer_objects - это convert_objects с переданными значениями по умолчанию (более или менее, может быть, некоторые специально настроены для DataFrame )?

infer_objects должно иметь параметров, просто выполните мягкое преобразование (в основном это вызовет maybe_convert_objects с параметрами по умолчанию

О'кей, в этом есть смысл. Я просто пытался понять и сопоставить в уме комментарии, сделанные во время этого обсуждения.

fyi, открыл # 16915 для infer_objects если кому-то интересно - в частности, если у вас есть пограничные тестовые примеры

Была ли эта страница полезной?
0 / 5 - 0 рейтинги