ny_quant: (Default)
[personal profile] ny_quant
Будь проклят тот день, когда я сел за баранку этого пылесоса! (с)

Я уже не буду говорить по сотому разу про семантику питона, индентацию и неудобный дебаггер. Самая феноменальная черта питона это непредсказуемое поведение, когда буквально один и тот же (скопипащенный) код в одном месте работает, а в другом нет, и понять в чем дело нельзя от слова никак. Сегодня у меня выдался особенно интересный день, в том смысле, что удалось наступить на одни и те же грабли дважды. Причем второй раз обошлось даже без копипасты, а прямо в одном и том же месте в цикле. Жил да был один dataframe, никого не трогал, починял примус. В одном месте к нему применялась операция groupby. И все шло хорошо пока я не поменял один из параметров, которые контролируют процесс наполнения этого dataframe. И вдруг ни с того ни с сего привет из черного ящика:

File "C:\Users\...\AppData\Local\Programs\Python\Python38\lib\site-packages\pandas\core\groupby\groupby.py", line 747, in get_group
raise KeyError(name)

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

print(pos)
positions = pos.groupby(by='sign')
print(positions)
longs = positions.get_group(1)

Цикл этот проработал очень много раз без проблем. На предыдущей перед обломом итерации напечаталось:

... ......exdate ......strike. sign
619 20170616.0 2200.0 1.0
620 20170616.0 2200.0 1.0
<много похожих строк>
792 20170421.0 2475.0 -1.0
793 20170421.0 2475.0 -1.0

pandas.core.groupby.generic.DataFrameGroupBy object at 0x000001DB90D0A760

(Точки я добавил в тело поста, чтобы подравнять заголовки колонок.) А в последний раз вот что:

... ......exdate ......strike. sign
619 20170616.0 2200.0 1.0
620 20170616.0 2200.0 1.0
<много похожих строк>
817 20170616.0 2445.0 -1.0
818 20170616.0 2445.0 -1.0

Empty DataFrame
Columns: [exdate, strike, sign]
Index: []

Empty DataFrame, вон оно как! Понятно теперь почему оттуда ничего нельзя вынуть и оно ломается. Но почему оно пустое??!! Колонка 'sign' есть? Есть. Числа в ней есть? Есть, причем в каждой строчке. Так какого же черта??? И что вообще люди делают в таких случаях? Идти дебаггером в библиотеку и искать почему оно ломается в groupby.py?

Пока что я схитрил и вместо

positions = pos.groupby(by='sign')
longs = positions.get_group(1)

написал

longs = pos[pos['sign'] > 0]

Это отлично сработало, причем для тех параметров, при которых groupby() работал без аварии, результаты получились идентичные. Это, конечно, хорошо, но не покидает ощущение, что ходишь по минному полю как ночью по тайге. Хотелось бы все же по ходу чему-то научиться и что-то понять. Особенно как искать черную кошку в темной комнате.

В комментарии особенно приглашается добродетельный юзер [livejournal.com profile] laoxia, который научил меня первым шагам с dataframe, а также все кто умеет питонить, особенно [livejournal.com profile] nefedor. Также я думаю, что скорее всего разбираются в теме [livejournal.com profile] misha_b и [livejournal.com profile] kobak. Извините если отвлек от важных дел.

Date: 2022-06-30 02:06 pm (UTC)
From: [identity profile] ny-quant.livejournal.com
Докладываю. Добавил

pos['sgn'] = pos['sgn'].astype(int)

сразу после объявления шапки

pos = pd.DataFrame(columns = ['exdate', 'strike', 'sgn'])

Это не помогло никак, т.к. первый же print(pos) выдал (спасибо @spamsink за тэг)

N       exdate  strike  sgn
0  20121019.0  1460.0 -1.0
1  20130621.0  1450.0  1.0


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

pos['sgn'] = pos['sgn'].astype(int)
print(pos)
positions = pos.groupby(by='sgn')
assert set(pos.sgn) == {1,-1}

Это сначала помогло, т.к. сначала значения таки стали целыми:


N       exdate  strike  sgn
0  20121019.0  1460.0 -1
1  20130621.0  1450.0  1


Но через какое то время все равно наступил кирдык:

assert set(pos.sgn) == {1,-1}
AssertionError

А если assert убрать, то потом тот же кирдык что и в начале: KeyError

Насчет "идеален" никак не могу согласиться. Идеально было в Матлабе, потому что там всё всегда работало с первого тыка и никаких таких сложностей не возникало ни разу за всю мою жизнь. Даже, извиняюсь, в Excel/VBA было гораздо лучше - пока было возможно там оставаться. А это, пардон, какая-то порнография.

Date: 2022-06-30 03:18 pm (UTC)
From: [identity profile] nefedor.livejournal.com

try:
. . assert set(pos.sgn) == {1, -1}
except AssertionError:
. . print(set(pos.sgn))
. . raise

Напечатает что стало с sgn в проблемном месте

Date: 2022-06-30 04:07 pm (UTC)
From: [identity profile] ny-quant.livejournal.com
Лапидарненько напечатало:

set()

Date: 2022-06-30 04:14 pm (UTC)
From: [identity profile] nefedor.livejournal.com

А если напечатать не set(pos.sign), а pos.shape? Типа это пустой датафрейм у нас или просто колонка убилась?

Edited Date: 2022-06-30 04:20 pm (UTC)

Date: 2022-06-30 04:27 pm (UTC)
From: [identity profile] ny-quant.livejournal.com
(0, 3)

Похоже что пустой. Ничего не понимаю. Пойду дебажить.

Date: 2022-06-30 04:29 pm (UTC)
From: [identity profile] nefedor.livejournal.com

То есть, три пустых колонки, и ни одной строки. Значит, скорее всего, не добавились данные из источника => глюк в исходных данных.

Date: 2022-06-30 04:59 pm (UTC)
From: [identity profile] ny-quant.livejournal.com
Разобрался. При этой комбинации параметров, в определенный день все позиции вдруг одновременно expire, а новых открыть не удается, потому что приближается конец базы данных, вот там и остается пустой набор позиций. На такой вариант я не рассчитывал и обычно этого не происходит.

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

Date: 2022-06-30 05:13 pm (UTC)
From: [identity profile] aklepatc.livejournal.com
Не понимаю, почему нельзя факторизовать (aggregate) пустое множество. Должно работать, просто ничего интересного не получится.

Date: 2022-06-30 05:20 pm (UTC)
From: [identity profile] nefedor.livejournal.com

Да, он должен был выдавать пустое множество групп и get_groups(1) тоже должно выдавать пустое множество. Это с точки зрения программиста.
Но видимо они посчитали, что для ученого попытка сгруппировать пустое множество это уже ошибка, и нужно ему об этом быстрее сказать. И что-то в этом есть, так как у Кванта была действительно непредвиденная ситуация, которая требует специальной обработки.

Date: 2022-06-30 09:42 pm (UTC)
From: [identity profile] aklepatc.livejournal.com
С точки зрения математика это тоже должно было сработать. Не только с тз программиста.

Фактор-множество (или множество классов эквивалентности?) — это довольно таки базовая идея.

Date: 2022-06-30 09:53 pm (UTC)
From: [identity profile] nefedor.livejournal.com

Сколько классов эквивалентности при группировке по значению пустой колонки? Видимо, ноль? Ну было бы логично, да.
Но тогда бомбанёт get() на любую группу. Либо вернёт None - но тогда должен бомбануть или усложниться дальнейший код...

Date: 2022-06-30 10:00 pm (UTC)
From: [identity profile] aklepatc.livejournal.com
Мы с вами помним, как работает dict.get. Было бы вполне по-питоновски сделать что-нибудь похожее:
groups.get(
    key, default=some_empty_iterable
)
как-то так...
Edited Date: 2022-07-01 12:39 am (UTC)

Date: 2022-07-04 05:46 pm (UTC)
From: [identity profile] nefedor.livejournal.com

Сделать можно, но если посмотреть на это с другой стороны, то все равно получится нарушение структуры в default случае если ожидаемая структура достаточно сложная. Что чревато все тем же сбоем в программе, который будет тот ко сложнее расследовать, так как он случится позже. Поэтому я не уверен, что это было бы много лучше.

Date: 2022-07-04 05:54 pm (UTC)
From: [identity profile] aklepatc.livejournal.com
I think, you have a point. Fail early and hard.

Date: 2022-06-30 05:25 pm (UTC)
From: [identity profile] ny-quant.livejournal.com
Именно! Причем фильтровать и резать его на слайсы, оказывается, вполне можно.

Date: 2022-06-30 05:45 pm (UTC)
From: [identity profile] malyj-gorgan.livejournal.com
А главное, оно ж прямо так и сказало: что ошибка в том, что "Empty DataFrame"
А мы тут изобретаем...

Date: 2022-06-30 05:59 pm (UTC)
From: [identity profile] ny-quant.livejournal.com
Да, я сдуру не понял к чему оно относилось.

Date: 2022-06-30 04:19 pm (UTC)
From: [identity profile] nefedor.livejournal.com

Или можно попробовать посмотреть на print(pos.info(verbose=True))

Edited Date: 2022-06-30 04:20 pm (UTC)

Date: 2022-06-30 04:26 pm (UTC)
From: [identity profile] malyj-gorgan.livejournal.com
Так а размер pos в этом месте какой? пустую таблицу не погруппируешь
Я настаиваю, что главная проблема в тoм, что group by нужен для aggregations, а фильтровать надо фильтрами

Date: 2022-06-30 05:00 pm (UTC)
From: [identity profile] ny-quant.livejournal.com
Разобрался: https://ny-quant.livejournal.com/959402.html?thread=13824426#t13824426

*Главная* проблема была не в этом but your point is well taken nevertheless.

Date: 2022-06-30 05:08 pm (UTC)
From: [identity profile] aklepatc.livejournal.com
Это прямо в точку! Плюсую...

Правда возникает вопрос: мы пытаемся починить этот конкретный косяк или пытаемся научить автора писать более идиоматический код? Или и того, и другого понемногу?
Edited Date: 2022-06-30 05:09 pm (UTC)

Profile

ny_quant: (Default)
ny_quant

January 2026

S M T W T F S
    123
45 6 7 8 9 10
11 12 1314151617
18192021222324
25262728293031

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jan. 14th, 2026 03:43 pm
Powered by Dreamwidth Studios