2016-06-26 3 views
7

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

Есть ли операция, похожая на поп, но для большого сегмента, которая одновременно удалит часть DataFrame и позволит мне назначить ее новому DataFrame? Что-то вроде этого:

# Assume I have initialized a DataFrame (called "all") which contains my large dataset, 
# with a boolean column called "test" which indicates whether a record should be used for 
# testing. 
print len(all) 
# 10000000 
test = all.pop_large_segment(all[test]) # not a real command, just a place holder 
print len(all) 
# 8000000 
print len(test)  
# 2000000 
+3

Насколько я знаю, в момент выполнения задания панда создает копию. Будет ли это работать, если вы просто сохраните показатели поезда и теста? – ayhan

+0

Не отвечая на вопрос, но, возможно, другие релевантные идеи: - Разве вы не можете разбить набор данных уже при загрузке? - Или использовать что-то вроде 'dask' (http://dask.pydata.org/en/latest/)? –

+0

Единственное, что я знаю, это загрузить их из таблицы HDF5 отдельно и выполнить разделение во время загрузки, т. Е. Загрузить некоторые строки сначала в качестве тренировки, а затем остальное как разделить - может обеспечить правильный ответ - это звучит правдоподобно ... – RexFuzzle

ответ

3

Если у вас есть пространство, чтобы добавить еще один столбец, можно добавить один со случайным значением, которое вы могли бы затем фильтровать на для тестирования. Здесь я использовал форму между 0 и 1, но вы могли бы использовать что угодно, если бы хотели другую пропорцию.

df = pd.DataFrame({'one':[1,2,3,4,5,4,3,2,1], 'two':[6,7,8,9,10,9,8,7,6], 'three':[11,12,13,14,15,14,13,12,11]}) 
df['split'] = np.random.randint(0, 2, size=len(df)) 

Конечно, это требует от вас есть место, чтобы добавить совершенно новый столбец - особенно, если ваши данные очень долго, может быть, вы не знаете.

Другой вариант будет работать, например, если ваши данные были в формате csv, и вы знали количество строк. Выполните аналогичные приведенным выше, с randomint, но передать этот список в skiprows аргумент панд read_csv():

num_rows = 100000 
all = range(num_rows) 

some = np.random.choice(all, replace=False, size=num_rows/2) 
some.sort() 
trainer_df = pd.read_csv(path, skiprows=some) 

rest = [i for i in all if i not in some] 
rest.sort() 
df = pd.read_csv(path, skiprows=rest) 

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

Чтобы сделать его еще более дружественным к памяти, вы можете загрузить подмножество тренера, подготовить модель, а затем переписать учебный информационный кадр с остальными данными, а затем применить модель. Вы столкнетесь с ногами some и rest, но вам никогда не придется загружать обе половинки данных одновременно.

1

Я бы сделал нечто похожее, как @ jeff-l, т. Е. Сохранил фрейм данных в файле. Когда вы читаете его как csv, используйте ключевое слово chunksize. Следующий сценарий иллюстрирует это:

import pandas 
import numpy 

test = 5 
m, n = 2*test, 3 

df = pandas.DataFrame(
    data=numpy.random.random((m, n)) 
) 

df['test'] = [0] * test + [1] * test 

df.to_csv('tmp.csv', index=False) 

for chunk in pandas.read_csv('tmp.csv', chunksize=test): 
    print chunk 
    del chunk 
+0

Это вытаскивает данные с диска в порядок, и набор учебных данных должен быть несмещенным. Хотя это сработает, если бы вы знали, что данные уже были случайным образом перетасованы на диск. – Jeff

1

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

Может быть, вы можете посмотреть на код в DataFrame.drop method и изменить его для того, чтобы изменить ваш DataFrame InPlace (которые уже делают метод drop) и получить другие Raws вернулись:

class DF(pd.DataFrame): 
    def drop(self, labels, axis=0, level=None, inplace=False, errors='raise'): 
     axis = self._get_axis_number(axis) 
     axis_name = self._get_axis_name(axis) 
     axis, axis_ = self._get_axis(axis), axis 

     if axis.is_unique: 
      if level is not None: 
       if not isinstance(axis, pd.MultiIndex): 
        raise AssertionError('axis must be a MultiIndex') 
       new_axis = axis.drop(labels, level=level, errors=errors) 
      else: 
       new_axis = axis.drop(labels, errors=errors) 
      dropped = self.reindex(**{axis_name: new_axis}) 
      try: 
       dropped.axes[axis_].set_names(axis.names, inplace=True) 
      except AttributeError: 
       pass 
      result = dropped 

     else: 
      labels = com._index_labels_to_array(labels) 
      if level is not None: 
       if not isinstance(axis, MultiIndex): 
        raise AssertionError('axis must be a MultiIndex') 
       indexer = ~axis.get_level_values(level).isin(labels) 
      else: 
       indexer = ~axis.isin(labels) 

      slicer = [slice(None)] * self.ndim 
      slicer[self._get_axis_number(axis_name)] = indexer 

      result = self.ix[tuple(slicer)] 

     if inplace: 
      dropped = self.ix[labels] 
      self._update_inplace(result) 
      return dropped 
     else: 
      return result, self.ix[labels] 

Который будет работать так:

df = DF({'one':[1,2,3,4,5,4,3,2,1], 'two':[6,7,8,9,10,9,8,7,6], 'three':[11,12,13,14,15,14,13,12,11]}) 

dropped = df.drop(range(5), inplace=True) 
# or : 
# partA, partB = df.drop(range(5)) 

Этот пример не является, вероятно, на самом деле памятью эффективной, но, может быть, вы можете выяснить, что-то лучше, используя какое-то объектно-ориентированный Soluti на этом.

+0

Думаю, для выполнения этой операции все равно потребуется дополнительное пространство, но, возможно, ее можно использовать итеративно с небольшими кусками. Это может быть быстрее, чем чтение из файла. – ayhan

+0

Это интересный подход. Хотя, опять же, он разбивает данные по порядку. В результатах 'partA' и' partB' вы получаете верхнюю половину данных в одной и нижней половине данных в другой. Смещенные образцы, если ваши данные случайным образом перетасовываются на диск. – Jeff

+0

@JeffL. Я полагаю, что на самом деле будет сложно получить случайные значения, используя список случайных и уникальных целых чисел, меньших количества функций, в качестве аргумента для функции drop (например, random.sample (range (len (df)), n_wanted_values) 'вместо непрерывного диапазона значений, как здесь, с диапазоном (5)') – mgc

Смежные вопросы