python: используйте strptime () для объекта, который не является нулевым заполнением - PullRequest
0 голосов
/ 24 мая 2018

У меня есть фрейм данных pandas, который имеет два столбца в формате объекта.Они содержат год (4 означает 2004 год) и месяц.Я хочу вычесть их.

start     end
4-oct     12-nov
dec-3     11-oct
jan-5     16-dec
12-oct    17-apr

Я пытался:

data['end'].apply(lambda x: datetime.strptime(repr(x), "'%y-%b'"))
data['end'].apply(lambda x: datetime.strptime(repr(x), "b'%y-%b'"))

Но они не работали.

  1. Как я могу работать с другим форматоми ненулевое заполнение в первом столбце ('% y-% b' и '% b-% y')
  2. Как применить strptime () к формату объекта?(может repr () преобразовать их в строку)?

Ответы [ 2 ]

0 голосов
/ 24 мая 2018

Есть несколько проблем с вашим кодом.

  • Вы используете %y, для которого требуется год из двух цифр, но некоторые из ваших лет являются цифрами.К счастью, они появляются только в start, а вы спрашиваете только, как разобрать end.Но если вы хотите также проанализировать start - или если ваши реальные данные имеют годы, состоящие из одной цифры, вам нужно это исправить.
  • Вы вызываете repr в строке, а затемпытаясь разобрать строку repr, вместо того, чтобы просто анализировать строку.(Ваши строки уже являются строками. object - это базовый класс каждого типа в Python, включая str. И это то, что Pandas использует для Серии, у которой нет хорошего типа, с которым она знает, как обращаться, например, int64или datetime64 - он просто хранит собственные объекты Python с любым собственным типом Python, например str.)
  • Некоторые из ваших строк представлены в формате месяц-год вместо года-месяца, поэтому тот же форматСтрока явно не собирается их анализировать.Вам нужно будет либо использовать какой-то эвристический парсер (может быть из dateutil), либо предварительно обработать их все в одном формате, либо написать функцию, которая пробует оба формата.
  • Одна из ваших строк не 'у меня даже есть действительный месяц.Вы не можете разобрать des-3 как месяц и год, потому что des не месяц.Я не уверен, что ты хочешь сделать с этим.Может быть, использовать значение без даты?
  • Apply не изменяет DataFrame на месте, он просто возвращает новый Series, который вы должны где-то хранить.

Соберите все вместе:

def parsedate(s):
    try:
        return datetime.strptime(s, '%y-%b')
    except ValueError:
        pass
    try:
        return datetime.strptime(s, '%b-%y')
    except ValueError:
        pass
    return datetime.now() # <whatever you actually want to do for des-3 here>
df.end = df.end.apply(parsedate)

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

И изКонечно, это превратит des-3 в now(), что, вероятно, не то, что вы хотите;вам нужно решить, чего вы на самом деле хотите.


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

MONTHS = {
    'jan': 1, 'feb': 2, 'mar': 3, 'apr': 4, 'may': 5, 'jun': 6,
    'jul': 7, 'aug': 8, 'sep': 9, 'oct': 10, 'nov': 11, 'dec': 12, 'des': 12 }
def parsedate(s):
    part1, _, part2 = s.partition('-')
    if part2.isdigit():
        part1, part2 = part2, part1
    return datetime(year=2000+int(part1), month=MONTHS[part2], day=1)
0 голосов
/ 24 мая 2018

Вы должны использовать %y-%b напрямую, а не на репре:

In [11]: df['end'].apply(lambda x: datetime.strptime(x, "%y-%b"))
Out[11]:
0   2012-11-01
1   2011-10-01
2   2016-12-01
3   2017-04-01
Name: end, dtype: datetime64[ns]

In [12]: pd.to_datetime(df["end"], format="%y-%b")  # alternatively/more efficient
Out[12]:
0   2012-11-01
1   2011-10-01
2   2016-12-01
3   2017-04-01
Name: end, dtype: datetime64[ns]

Как только они оба в пандах datetime64 Series, вы можете вычесть их с помощью -.


Чтобы исправить однозначный год (в начальном столбце), я бы использовал регулярное выражение для их нормализации:

In [21]: df["start"].replace({"^(\d-.*)$": "0\\g<1>", "^(.*)-(\d)$": "0\\g<2>-\\g<1>"}, regex=True)
Out[21]:
0    04-oct
1    03-dec
2    05-jan
3    12-oct
Name: start, dtype: object

Затем вы можете применить приведенный выше формат.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...