ny_quant: (Default)
ny_quant ([personal profile] ny_quant) wrote2022-06-29 06:13 pm
Entry tags:

Помощь зала

Будь проклят тот день, когда я сел за баранку этого пылесоса! (с)

Я уже не буду говорить по сотому разу про семантику питона, индентацию и неудобный дебаггер. Самая феноменальная черта питона это непредсказуемое поведение, когда буквально один и тот же (скопипащенный) код в одном месте работает, а в другом нет, и понять в чем дело нельзя от слова никак. Сегодня у меня выдался особенно интересный день, в том смысле, что удалось наступить на одни и те же грабли дважды. Причем второй раз обошлось даже без копипасты, а прямо в одном и том же месте в цикле. Жил да был один 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. Извините если отвлек от важных дел.

[identity profile] aklepatc.livejournal.com 2022-06-29 11:23 pm (UTC)(link)
Pandas — это, всё таки, вещь в себе. Не совсем "Питон как он есть".

Извините, я понимаю, что мой комментарий не особо помогает. Я писал на Питоне много лет, но очень мало соприкасался с pandas.

[identity profile] nefedor.livejournal.com 2022-06-29 11:43 pm (UTC)(link)
Я вообще в Пандас не работал, я в numpy.
Но я думаю, что у тебя где-то в данных есть бяка. И в той строке где бяка, sign не 1 и не -1. В результате оно делает еще одну группу с sign=something_strange но строку туда не добавляет, так как порченную строку ей добавлять неохота - получается пустая категория, на что она и жалуется.
Вот за это я не люблю DataFrames :)
Вторая версия: у тебя категории по floating point, какой-то sign может быть не 1.0 в точности, а 1.0 + 1.e-alot или что-то похожее. Но скорее всего не это, а предыдущее.
Чтобы проверить ты можешь сделать set или dict:
d = {} # or s = set()
for row in data:
. . sign = row[-1]
. . d[sign] = 3.1415 # or s.add(sign)
а потом распечатать какие ключи получились:
print(d.keys()) # or print(s)
И если там есть бяка в sign, ты ее увидишь.

Как вообще бороться с такими вещами: гуглить.
Вот, например по "pandas groupby gives empty dataframe":
https://stackoverflow.com/questions/62303505/group-by-returns-empty-dataframe-and-no-error
https://github.com/pandas-dev/pandas/issues/743

Edited 2022-06-29 23:44 (UTC)

[identity profile] ny-quant.livejournal.com 2022-06-30 12:20 am (UTC)(link)
Спасибо!

Я проверил: все 1.0 или -1.0, хотя я и не понимаю какая разница, т.к. groupby должен группировать в любом случае, нет?

Да и не может там быть больше ничего, т.к. все записи добавляются в dataframe командами

pos.loc[posIndex] = [exdate, strike, 1]
pos.loc[posIndex] = [exdate, strike, -1]

Я вообще-то гуглил но ничего толкового не нашел. Завтра попробую почитать то, что нашел ты. По первой ссылке пока что ничего не понял. Не владею ни лямбдами ни join.

[identity profile] nefedor.livejournal.com 2022-06-30 12:47 am (UTC)(link)
Еще пишут, названия колонок могут быть проблемой:
https://stackoverflow.com/questions/56815086/pandas-groupby-is-giving-keyerror-even-when-the-key-exists

И тут что-то заумное про индексы:
https://github.com/modin-project/modin/issues/1900
(deleted comment)

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-06-30 03:34 (UTC) - Expand

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-06-30 14:50 (UTC) - Expand

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-06-30 16:19 (UTC) - Expand

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-06-30 16:49 (UTC) - Expand

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-06-30 17:01 (UTC) - Expand

(no subject)

[identity profile] aklepatc.livejournal.com - 2022-06-30 13:57 (UTC) - Expand

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-06-30 14:18 (UTC) - Expand

(no subject)

[identity profile] Алексей Орлов - 2022-06-30 15:25 (UTC) - Expand

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-06-30 14:40 (UTC) - Expand

(no subject)

[identity profile] aklepatc.livejournal.com - 2022-06-30 14:51 (UTC) - Expand

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-06-30 14:53 (UTC) - Expand

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-06-30 16:16 (UTC) - Expand

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-07-01 02:20 (UTC) - Expand

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-06-30 03:41 (UTC) - Expand

[identity profile] malyj-gorgan.livejournal.com 2022-06-30 01:02 am (UTC)(link)
pandas are like that, yes :) прямых ответов у них не бывает

Мой внутренний самоцензор говорит:
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
тогда, как сломается, можно на него посмотреть

[identity profile] malyj-gorgan.livejournal.com 2022-06-30 01:05 am (UTC)(link)
А, Нефедор выше уже все написал

[identity profile] ny-quant.livejournal.com 2022-06-30 03:21 am (UTC)(link)
Спасибо, я забыл, что вы тоже data scientist.

Заменил 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) впендюрить?


[identity profile] malyj-gorgan.livejournal.com 2022-06-30 06:08 am (UTC)(link)
В какой то момент print(pos) выдает все эти 1.0, -1.0
можно проверить 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)

[personal profile] ak_47 - 2022-06-30 08:17 (UTC) - Expand

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-06-30 14:06 (UTC) - Expand

(no subject)

[identity profile] nefedor.livejournal.com - 2022-06-30 15:18 (UTC) - Expand

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-06-30 16:07 (UTC) - Expand

(no subject)

[identity profile] nefedor.livejournal.com - 2022-06-30 16:14 (UTC) - Expand

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-06-30 16:27 (UTC) - Expand

(no subject)

[identity profile] nefedor.livejournal.com - 2022-06-30 16:29 (UTC) - Expand

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-06-30 16:59 (UTC) - Expand

(no subject)

[identity profile] aklepatc.livejournal.com - 2022-06-30 17:13 (UTC) - Expand

(no subject)

[identity profile] nefedor.livejournal.com - 2022-06-30 17:20 (UTC) - Expand

(no subject)

[identity profile] aklepatc.livejournal.com - 2022-06-30 21:42 (UTC) - Expand

(no subject)

[identity profile] nefedor.livejournal.com - 2022-06-30 21:53 (UTC) - Expand

(no subject)

[identity profile] aklepatc.livejournal.com - 2022-06-30 22:00 (UTC) - Expand

(no subject)

[identity profile] nefedor.livejournal.com - 2022-07-04 17:46 (UTC) - Expand

(no subject)

[identity profile] aklepatc.livejournal.com - 2022-07-04 17:54 (UTC) - Expand

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-06-30 17:25 (UTC) - Expand

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-06-30 17:59 (UTC) - Expand

(no subject)

[identity profile] nefedor.livejournal.com - 2022-06-30 16:19 (UTC) - Expand

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-06-30 17:00 (UTC) - Expand

(no subject)

[identity profile] aklepatc.livejournal.com - 2022-06-30 17:08 (UTC) - Expand
ak_47: (default)

[personal profile] ak_47 2022-06-30 01:09 am (UTC)(link)
Как уже выше отметили, это скорее не проблема питона как такового, а конкретные тонкости Pandas.

Без контекста тяжело понять почему. Думаю, nefedor прав насчёт мусора в данных.
А каким дебаггером вы пользуетесь? В чём неудобство?

[identity profile] ny-quant.livejournal.com 2022-06-30 03:04 am (UTC)(link)
По-моему никакого мусора нет, быть не может, и главное на операцию groupby это по-моему влиять не должно, т.е. она должна бы работать с любым мусором. Иначе это баг. После стольких лет развития и миллионов пользователей? Правда?

IDLE 3.8

Я привык к тому, что если навести мышку на переменную, то покажут что у нее внутре. А тут хрен тебе. А если это не просто скаляр, а скажем dataframe, то и вовсе не покажут, надо делать print чтобы что-то увидеть - тоска смертная. Вверх-вниз (up/down) по стеку ходить нельзя, или я пока не сообразил как.
ak_47: (default)

[personal profile] ak_47 2022-06-30 03:34 am (UTC)(link)
Сейчас такое качество софта везде, что уже ничего нельзя предполагать. Хотя насчёт миллионов пользователей и многих лет развития согласен. В популярных продуктах явные баги довольно быстро находят и оперативно чинят.

IDLE 3.8

Это жесткач. Я бы посоветовал хоть что-нибудь более юзабильное. Например, VSCode с плагином для питона. Там будет и нормальный дебаггер, и более приятный REPL.

С Pandas я никогда не работал, но эти трудности будут с любой продвинутой библиотекой. Особенно если там оптимизации для производительности. Похоже что dataframe это абстракция, которая сама не содержит данные, а только даёт доступ. Поэтому и не видно ничего.

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-06-30 03:51 (UTC) - Expand

(no subject)

[personal profile] ak_47 - 2022-06-30 04:09 (UTC) - Expand

(no subject)

[identity profile] iv6.livejournal.com - 2022-06-30 08:59 (UTC) - Expand

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-06-30 14:36 (UTC) - Expand

(no subject)

[identity profile] iv6.livejournal.com - 2022-06-30 19:16 (UTC) - Expand

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-06-30 19:56 (UTC) - Expand

(no subject)

[identity profile] iv6.livejournal.com - 2022-07-01 04:56 (UTC) - Expand
(deleted comment)

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-07-01 13:31 (UTC) - Expand
spamsink: (blessed)

[personal profile] spamsink 2022-06-30 08:16 am (UTC)(link)
Я в питон не умею, но умею в тег <pre>
N     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

[identity profile] hervejoncour.livejournal.com 2022-06-30 09:14 am (UTC)(link)
Если до сих пор не нашли решения post the question in stackoverflow.com

[identity profile] ny-quant.livejournal.com 2022-06-30 02:11 pm (UTC)(link)
Тут пока что не вполне ясно что именно спрашивать. В данном случае формат интерактивной беседы подходит куда лучше, да и народ вроде квалифицированный.

[identity profile] mi-b.livejournal.com 2022-06-30 11:25 am (UTC)(link)
а что она говорит если прямо перед проблемным groupby напечатать print(pos.isnull().values.any()) ?

[identity profile] ny-quant.livejournal.com 2022-06-30 01:15 pm (UTC)(link)
False - каждый раз, в том числе прямо перед обломом.

[identity profile] luxs135.livejournal.com 2022-06-30 11:32 am (UTC)(link)
Панда вообще немного глючная, а если учесть разное поведение в разных версиях, то вообще катастрофа.

[identity profile] luxs135.livejournal.com 2022-06-30 12:49 pm (UTC)(link)
А вообще, немного подумав - группировать по значению флоат - дело нехорошее.

[identity profile] ny-quant.livejournal.com 2022-06-30 02:14 pm (UTC)(link)
Теоретически соглашусь. Я чесгря не знал, что они окажутся флоат, предполагал, что инт. Но интересно, что оно довольно долго работало пока не сломалось, поэтому думаю, что не из-за этого, тем более, что предложенные фиксы пока что не помогают.

[identity profile] laoxia.livejournal.com 2022-06-30 12:21 pm (UTC)(link)
Я, конечно, так сразу диагноз не поставлю, но слова "поменял один из параметров, которые контролируют процесс наполнения этого dataframe" и KeyError в get_group намекают, что стоит посмотреть, какие там есть группы, например, написав

print(positions.groups.keys())

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

[identity profile] ny-quant.livejournal.com 2022-06-30 02:35 pm (UTC)(link)
Значит, имеем:

pos['sgn'] = pos['sgn'].astype(int)
positions = pos.groupby(by='sgn')
print(positions.groups.keys())
longs = positions.get_group(1)

На печать выводится:

dict_keys([-1, 1])
...
dict_keys([-1, 1])
dict_keys([])

После чего, натурально, кирдык.

[identity profile] ticklish-frog.livejournal.com 2022-06-30 04:28 pm (UTC)(link)
Непонятно, у Вас один print, A выдаётся много чего.

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-06-30 16:43 (UTC) - Expand

[identity profile] snake-d-ha.livejournal.com 2022-06-30 02:04 pm (UTC)(link)
Image

[identity profile] Алексей Орлов (from livejournal.com) 2022-06-30 02:44 pm (UTC)(link)
Spyder уже упомянули. Вещь неплохая, и даже заточенная (до некоторой степени) под data science. Не стал бы, впрочем, рекомендовать казуальному пользователю. Само пользователю сопли не подтирает. Если каждый день не пользоваться, всякая фигня может портить настроение.

Самый trouble free вариант - PyCharm. И для профессионала годится, и для любителя. Бесплатный, no strings attached (есть родственные продукты, подписные, но PyCharm Community бесплатен для кого угодно). Гуманен к пользователю.

Начинать отсюда: https://www.jetbrains.com/toolbox-app/
Edited 2022-06-30 14:47 (UTC)

[identity profile] ny-quant.livejournal.com 2022-06-30 02:59 pm (UTC)(link)
Спасибо. А как там с отладчиком?

(no subject)

[identity profile] Алексей Орлов - 2022-06-30 15:03 (UTC) - Expand

(no subject)

[identity profile] Алексей Орлов - 2022-06-30 15:09 (UTC) - Expand

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-06-30 15:39 (UTC) - Expand

(no subject)

[identity profile] Алексей Орлов - 2022-06-30 15:45 (UTC) - Expand

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-06-30 16:11 (UTC) - Expand

(no subject)

[identity profile] Алексей Орлов - 2022-06-30 18:21 (UTC) - Expand

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-06-30 21:20 (UTC) - Expand

(no subject)

[identity profile] Алексей Орлов - 2022-07-01 05:44 (UTC) - Expand

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-07-01 13:47 (UTC) - Expand

(no subject)

[identity profile] Алексей Орлов - 2022-07-01 14:31 (UTC) - Expand

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-07-01 14:48 (UTC) - Expand

(no subject)

[identity profile] Алексей Орлов - 2022-07-01 15:00 (UTC) - Expand

(no subject)

[identity profile] nefedor.livejournal.com - 2022-06-30 16:01 (UTC) - Expand

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-06-30 16:13 (UTC) - Expand

(no subject)

[identity profile] nefedor.livejournal.com - 2022-06-30 15:19 (UTC) - Expand

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-06-30 15:39 (UTC) - Expand

(no subject)

[identity profile] Алексей Орлов - 2022-07-05 09:36 (UTC) - Expand

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-07-05 15:16 (UTC) - Expand

(no subject)

[identity profile] Алексей Орлов - 2022-07-05 17:17 (UTC) - Expand

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-07-05 17:34 (UTC) - Expand

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-07-06 23:10 (UTC) - Expand

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-07-07 17:46 (UTC) - Expand

(no subject)

[identity profile] ny-quant.livejournal.com - 2022-07-06 23:12 (UTC) - Expand

(no subject)

[personal profile] ak_47 - 2022-07-02 01:01 (UTC) - Expand

(no subject)

[identity profile] nefedor.livejournal.com - 2022-07-04 17:47 (UTC) - Expand

(no subject)

[personal profile] ak_47 - 2022-07-04 21:23 (UTC) - Expand