Есть ли у панд ограничения по размеру фильтров?

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

    game_id season  home_team   away_team   net_ortg    net_drtg    clock   period  home    visitor ... total_seconds_elapsed   win lead    p_1 p_2 p_3 p_4 p_5 p_6 total_pts
627168  401173715   2020    Air Force   UC Riverside    12.0    10.5    00:06:34    1   37  24  ... 806 1   13  1   0   0   0   0   0   61
320163  401174714   2020    Arkansas State  Idaho   11.4    0.4 00:01:42    2   76  67  ... 2298    1   9   0   1   0   0   0   0   143
26942   401169867   2020    Vanderbilt  Tulsa   1.5 10.9    00:07:50    1   24  18  ... 730 0   6   1   0   0   0   0   0   42
213142  401170184   2020    La Salle    Wagner  2.3 -13.5   00:10:19    2   57  36  ... 1781    1   21  0   1   0   0   0   0   93
1631866 401255594   2021    Virginia Tech   South Florida   8.4 -1.5    00:19:32    1   2   0   ... 28  1   2   1   0   0   0   0   0   2
1644302 401263600   2021    Nebraska    South Dakota    1.2 -8.1    00:14:51    1   9   11  ... 309 1   -2  1   0   0   0   0   0   20
1181057 401170704   2020    Colorado    Stanford    4.7 3.1 00:14:22    1   6   4   ... 338 1   2   1   0   0   0   0   0   10
1670578 401266749   2021    Texas Tech  Troy    15.2    -17.9   00:07:54    2   67  33  ... 1926    1   34  0   1   0   0   0   0   100
27199   401170392   2020    Florida Gulf Coast  Campbell    -5.6    -2.0    00:17:46    1   2   0   ... 134 0   2   1   0   0   0   0   0   2
1588187 401262682   2021    UNLV    Montana State   4.5 -0.8    00:02:54    1   23  39  ... 1026    0   -16 1   0   0   0   0   0   62

Я использую test_train_split из sklearn, чтобы разделить фрейм данных на game_id, чтобы я мог выполнять некоторые задачи ML.

train_id, test_id = train_test_split(list(df.game_id), test_size=0.1)
train_mask = df['game_id'].isin(list(train_id))
test_mask = df['game_id'].isin(list(test_id))
print(df.shape)
print(len(train_id))
print(len(test_id))
>>(1326422, 22)
>>1193779
>>132643

Вот странная вещь (или, по крайней мере, часть, которую я не понимаю):

>>train_mask.describe()
count     1326422
unique          1
top          True
freq      1326422
Name: game_id, dtype: object

>>test_mask.describe()
count     1326422
unique          1
top          True
freq      1326422
Name: game_id, dtype: object

Хорошо, но что, если я сделаю то же самое, но ограничу размер train_id:

train_mask = df['game_id'].isin(list(train_id[0:100]))
train_mask.describe()
count     1326422
unique          2
top         False
freq      1302107
Name: game_id, dtype: object

И просто проверить еще раз, используя индексацию массива в полном списке:

train_mask = df['game_id'].isin(list(train_id[0:-1]))
train_mask.describe()
count     1326422
unique          1
top          True
freq      1326422
Name: game_id, dtype: object

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

Обновлено: похоже, что точный размер, в котором происходит это поведение, составляет 54 665:

>>train_mask = df['game_id'].isin(list(train_id[0:54665]))
>>train_mask.describe()
count     1326422
unique          2
top          True
freq      1326180
Name: game_id, dtype: object

>>train_mask = df['game_id'].isin(list(train_id[0:54666]))
>>train_mask.describe()
count     1326422
unique          1
top          True
freq      1326422
Name: game_id, dtype: object

Действительно странно!

1
0
236
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Я считаю, что ваша маска представляет собой набор значений True False, которые являются длиной DataFrame. Когда вы ограничиваете размер train_id, вы просто уменьшаете количество значений True, а не уменьшаете длину маски. Попробуйте выполнить следующие действия для подтверждения:

print(len(df['game_id'].isin(list(train_id[0:100]))))
print(len(df['game_id'].isin(list(train_id[0:-1]))))

А затем, чтобы увидеть, сколько у вас истинных значений (сумма работает здесь, потому что True оценивается как 1, а False как 0):

df['game_id'].isin(list(train_id[0:100])).sum()
df['game_id'].isin(list(train_id[0:-1])).sum()

Когда я пытаюсь использовать маску для фильтрации df, это не работает. Это проблема. Он просто сохраняет все строки. Когда я это делаю df[df['game_id'].isin(list(train_id))].shape, получается точно такая же форма, как и у самого df.

Evan Zamir 18.12.2020 20:37

Pd.Series.isin возвращает логический ряд той же длины, что и все, что вы проверяли. Таким образом, вы не измените форму чего-либо, пока не нарежете свой DataFrame: df_train = df[train_mask]

Чтобы прояснить несколько вещей, вывод describe отображает следующее:

import pandas as pd
s = pd.Series([True]*10 + [False]*6)

s.describe()
#count       16    # length of the Series
#unique       2    # Number of unique values in the Series
#top       True    # Most common value
#freq        10    # How many times does the most common value appear
#dtype: object

Таким образом, проверка разных идентификаторов никогда не изменит количество. Но уникальные, верхние и частотные меняются, чтобы отразить тот факт, что меняется сама ваша маска.

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

Evan Zamir 18.12.2020 20:29

@EvanZamir Я могу почти гарантировать, что проблема не в пандах, налагающих какие-либо ограничения, а в неправильном понимании данных или того, как работает метод. Например, если game_id сильно дублируется в вашем DataFrame, то, как вы делаете выборку, вероятно, один и тот же game_id появляется как в обучающем, так и в тестовом наборах. т.е. может быть, вы хотите что-то вроде train_test_split(df.game_id.unique().tolist(), 0.1)

ALollz 18.12.2020 20:40

Ах! Да, это проблема. Я тупой лол.

Evan Zamir 18.12.2020 20:42

Я просто добавляю решение ALollz, чтобы показать вам кадры данных (так что примите его/ее ответ). Как уже говорилось, это вернет серию True и False:

import pandas as pd

df = pd.DataFrame(
[['401173715',   '2020',    'Air Force'],   
['401174714' ,  '2020',    'Arkansas State'],  
['401169867' ,  '2020',    'Vanderbilt'],
['401170184'  , '2020',    'La Salle'],
['401255594'  , '2021',    'Virginia Tech'],
['401263600'  , '2021',    'Nebraska'],
['401170704'  , '2020',    'Colorado'],
['401266749'  , '2021',    'Texas Tech'],
['401170392'  , '2020',    'Florida Gulf'],
['401262682'  , '2021',    'UNLV']],
columns = ['game_id', 'season',  'home_team'   ])
from sklearn.model_selection import train_test_split

train_id, test_id = train_test_split(list(df.game_id), test_size=0.1)


train_mask = df['game_id'].isin(list(train_id))
test_mask = df['game_id'].isin(list(test_id))

Так что описание правильное, как описывает ALollz. Имеет 2 уникальных значения (True, False), а счетчики верхних значений либо True, либо False, в зависимости от того, на какую маску вы смотрите, и количество одинаково, а частота будет меняться. теперь, если вы ограничите количество строк и не включите последнюю строку (индекс 10), у вас останется только 1 уникальное значение в каждом наборе данных.

Теперь я предполагаю, что вы хотите получить эти строки (где это правда). Итак, вам нужно изменить синтаксис на:

train_mask = df[df['game_id'].isin(list(train_id))]
test_mask = df[df['game_id'].isin(list(test_id))]

Это даст вам 2 фрейма данных с train_ids и тестовыми идентификаторами:

Так что поиграйте с этим кодом, чтобы увидеть:

import pandas as pd

df = pd.DataFrame(
[['401173715',   '2020',    'Air Force'],   
['401174714' ,  '2020',    'Arkansas State'],  
['401169867' ,  '2020',    'Vanderbilt'],
['401170184'  , '2020',    'La Salle'],
['401255594'  , '2021',    'Virginia Tech'],
['401263600'  , '2021',    'Nebraska'],
['401170704'  , '2020',    'Colorado'],
['401266749'  , '2021',    'Texas Tech'],
['401170392'  , '2020',    'Florida Gulf'],
['401262682'  , '2021',    'UNLV']],
columns = ['game_id', 'season',  'home_team'   ])
from sklearn.model_selection import train_test_split

train_id, test_id = train_test_split(list(df.game_id), test_size=0.1)


train_mask = df['game_id'].isin(list(train_id))
test_mask = df['game_id'].isin(list(test_id))

df_train = df[train_mask]
df_test = df[test_mask]

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

Evan Zamir 18.12.2020 20:30

Какая у тебя версия панд?

chitown88 18.12.2020 20:45

Извините, это была моя ошибка, а не панды. Мне нужно было сделать df.game_id.unique(), поскольку, должно быть, происходило то, что одни и те же идентификаторы отбирались в поезде и тесте. Виноват!

Evan Zamir 18.12.2020 20:46

Ах поймал. Прохладный.

chitown88 18.12.2020 21:05
Ответ принят как подходящий

Если game_id не является уникальным идентификатором, вы можете получить одни и те же game_id как в поезде, так и в тестовом наборе, что, вероятно, приведет к тому, что одни и те же записи окажутся как в поезде, так и в тестовом наборе. Вместо этого создал train_test_split на уникальных game_ids.

train_id, test_id = train_test_split(df.game_id.unique(), test_size=0.1)

Извините, да, это не был уникальный идентификатор. Так что это было причиной проблемы. Мне нужно было разделить на уникальные идентификаторы.

Evan Zamir 18.12.2020 20:50

Это действительно полезная информация - обновил мой ответ, основываясь на том, что может быть вашей истинной проблемой.

Matthew Cox 18.12.2020 20:57

Другие вопросы по теме