Помощь зала
Jun. 29th, 2022 06:13 pmБудь проклят тот день, когда я сел за баранку этого пылесоса! (с)
Я уже не буду говорить по сотому разу про семантику питона, индентацию и неудобный дебаггер. Самая феноменальная черта питона это непредсказуемое поведение, когда буквально один и тот же (скопипащенный) код в одном месте работает, а в другом нет, и понять в чем дело нельзя от слова никак. Сегодня у меня выдался особенно интересный день, в том смысле, что удалось наступить на одни и те же грабли дважды. Причем второй раз обошлось даже без копипасты, а прямо в одном и том же месте в цикле. Жил да был один 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() работал без аварии, результаты получились идентичные. Это, конечно, хорошо, но не покидает ощущение, что ходишь по минному полю как ночью по тайге. Хотелось бы все же по ходу чему-то научиться и что-то понять. Особенно как искать черную кошку в темной комнате.
В комментарии особенно приглашается добродетельный юзер
laoxia, который научил меня первым шагам с dataframe, а также все кто умеет питонить, особенно
nefedor. Также я думаю, что скорее всего разбираются в теме
misha_b и
kobak. Извините если отвлек от важных дел.
Я уже не буду говорить по сотому разу про семантику питона, индентацию и неудобный дебаггер. Самая феноменальная черта питона это непредсказуемое поведение, когда буквально один и тот же (скопипащенный) код в одном месте работает, а в другом нет, и понять в чем дело нельзя от слова никак. Сегодня у меня выдался особенно интересный день, в том смысле, что удалось наступить на одни и те же грабли дважды. Причем второй раз обошлось даже без копипасты, а прямо в одном и том же месте в цикле. Жил да был один 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() работал без аварии, результаты получились идентичные. Это, конечно, хорошо, но не покидает ощущение, что ходишь по минному полю как ночью по тайге. Хотелось бы все же по ходу чему-то научиться и что-то понять. Особенно как искать черную кошку в темной комнате.
В комментарии особенно приглашается добродетельный юзер
no subject
Date: 2022-06-30 01:02 am (UTC)Мой внутренний самоцензор говорит:
a. rename "sign" to smth non-keywordy, like "sgn" or "_sign"
b. ... and make sure it's .astype(int), grouping by floats is bad form
...
and then wrap the whole thing into try/except
тогда, как сломается, можно на него посмотреть
no subject
Date: 2022-06-30 01:05 am (UTC)no subject
Date: 2022-06-30 03:21 am (UTC)Заменил sign->sgn. Не помогло.
Про astype(int) не понял. Извините, я начинающий.
Пока что код такой:
pos = pd.DataFrame(columns = ['exdate', 'strike', 'sgn'])
pos.loc[posIndex] = [exdate, strike, 1]
...
pos.loc[posIndex] = [exdate, strike, -1]
Куда мне этот astype(int) впендюрить?
no subject
Date: 2022-06-30 06:08 am (UTC)можно проверить pos.sgn.dtype, он будет dtype('float64')
тогда надо набрать
>> pos['sgn'] = pos['sgn'].astype(int)
pos.sgn.dtype станет dtype('int64')
В этом месте очень хорошо бы для проверки написать такое:
assert pos.shape[0]>0 # this checks that you're grouping by non-empty df
assert set(pos.sgn) - {1,-1} == set() # this checks that column 'sgn' only has 1 and -1 values
ну или, если по условию задачи надо, чтобы были оба значения, и плюс и минус 1, то всего одна проверка:
assert set(pos.sgn) == {1,-1}
в момент, когда колонка сгн будет не в порядке, ассерт выдаст ошибку, и можно будет смотреть, че не так
Ну, или, как я писал, ту строчку, где сейчас ошибка, завернуть в try/except
Но, честно говоря, если вы используете groupby как фильтр для sgn= 1 or -1, то это неправильно, групбай для другого, в частности, если нету группы 1, то будет нехорошо. groupby создает сгруппированный объект для последующей аггрегации. А то, как вы написали работающий вариант -- как раз правильно
Или, если нравятся (как мне) explicit statements:
longs = pos.query("sgn == 1")
Кроме того, еще от внутреннего цензора, assignment через .loc — still a bad form. Не скажу, что там точно проблема, но там в разных версиях панд разные моменты то deprecated, то not supported...
В общем, если только вам не особенно важен тот или иной индекс, надо создать либо list of dicts
X = [{'exdate' : (скаляр), 'strike': <тоже скаляр>, 'sgn': (1 или -1)} ]
or, better, a dict where the values are list
X = {'exdate' : [list of values], 'strike': [another list of values, 'sgn': [list of ones and minus ones]}
после чего определеть датафрейм как
pos = pd.DataFrame(X)
Вообще, Python для ad hoc coding — почти идеален. Продакшен уже о другом, а прототипы идей на коленке клепать --милое дело
no subject
Date: 2022-06-30 08:17 am (UTC)no subject
Date: 2022-06-30 03:41 pm (UTC)no subject
Date: 2022-06-30 02:06 pm (UTC)pos['sgn'] = pos['sgn'].astype(int)
сразу после объявления шапки
pos = pd.DataFrame(columns = ['exdate', 'strike', 'sgn'])
Это не помогло никак, т.к. первый же print(pos) выдал (спасибо @spamsink за тэг)
Тогда я добавил то же самое прямо туда, где все происходит (хотя непонятно почему надо каждый раз повторять что там целое) и туда же влепил и assert:
pos['sgn'] = pos['sgn'].astype(int)
print(pos)
positions = pos.groupby(by='sgn')
assert set(pos.sgn) == {1,-1}
Это сначала помогло, т.к. сначала значения таки стали целыми:
Но через какое то время все равно наступил кирдык:
assert set(pos.sgn) == {1,-1}
AssertionError
А если assert убрать, то потом тот же кирдык что и в начале: KeyError
Насчет "идеален" никак не могу согласиться. Идеально было в Матлабе, потому что там всё всегда работало с первого тыка и никаких таких сложностей не возникало ни разу за всю мою жизнь. Даже, извиняюсь, в Excel/VBA было гораздо лучше - пока было возможно там оставаться. А это, пардон, какая-то порнография.
no subject
Date: 2022-06-30 03:18 pm (UTC)try:
. . assert set(pos.sgn) == {1, -1}
except AssertionError:
. . print(set(pos.sgn))
. . raise
Напечатает что стало с sgn в проблемном месте
no subject
Date: 2022-06-30 04:07 pm (UTC)set()
no subject
Date: 2022-06-30 04:14 pm (UTC)А если напечатать не set(pos.sign), а pos.shape? Типа это пустой датафрейм у нас или просто колонка убилась?
no subject
Date: 2022-06-30 04:27 pm (UTC)Похоже что пустой. Ничего не понимаю. Пойду дебажить.
no subject
Date: 2022-06-30 04:29 pm (UTC)То есть, три пустых колонки, и ни одной строки. Значит, скорее всего, не добавились данные из источника => глюк в исходных данных.
no subject
Date: 2022-06-30 04:59 pm (UTC)Все равно могли бы сказать, что df пустой, а не давать непонятный KeyError.
no subject
Date: 2022-06-30 05:13 pm (UTC)no subject
Date: 2022-06-30 05:20 pm (UTC)Да, он должен был выдавать пустое множество групп и get_groups(1) тоже должно выдавать пустое множество. Это с точки зрения программиста.
Но видимо они посчитали, что для ученого попытка сгруппировать пустое множество это уже ошибка, и нужно ему об этом быстрее сказать. И что-то в этом есть, так как у Кванта была действительно непредвиденная ситуация, которая требует специальной обработки.
no subject
Date: 2022-06-30 09:42 pm (UTC)Фактор-множество (или множество классов эквивалентности?) — это довольно таки базовая идея.
no subject
Date: 2022-06-30 09:53 pm (UTC)Сколько классов эквивалентности при группировке по значению пустой колонки? Видимо, ноль? Ну было бы логично, да.
Но тогда бомбанёт get() на любую группу. Либо вернёт None - но тогда должен бомбануть или усложниться дальнейший код...
no subject
Date: 2022-06-30 10:00 pm (UTC)как-то так...groups.get( key, default=some_empty_iterable )(no subject)
From:(no subject)
From:no subject
Date: 2022-06-30 05:25 pm (UTC)no subject
Date: 2022-06-30 05:45 pm (UTC)А мы тут изобретаем...
no subject
Date: 2022-06-30 05:59 pm (UTC)no subject
Date: 2022-06-30 04:19 pm (UTC)Или можно попробовать посмотреть на print(pos.info(verbose=True))
no subject
Date: 2022-06-30 04:26 pm (UTC)Я настаиваю, что главная проблема в тoм, что group by нужен для aggregations, а фильтровать надо фильтрами
no subject
Date: 2022-06-30 05:00 pm (UTC)*Главная* проблема была не в этом but your point is well taken nevertheless.
no subject
Date: 2022-06-30 05:08 pm (UTC)Правда возникает вопрос: мы пытаемся починить этот конкретный косяк или пытаемся научить автора писать более идиоматический код? Или и того, и другого понемногу?