2016-02-18 4 views
2

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

def outer_method(): 
    ... do outer scope stuff here 
    df = pd.DataFrame(columns=['A','B','C','D']) 
    def recursive_method(arg): 
     ... do local stuff here 
     # func returns a data frame to be appended to empty data frame 
     results_df = func(args) 
     df.append(results_df, ignore_index=True) 
     return results 
recursive_method(arg) 
return df 

Однако это НЕ работает. df всегда пуст, если я присоединяюсь к нему таким образом.

Я нашел ответ на свою проблему здесь: appending-to-an-empty-data-frame-in-pandas ... это работает, ЕСЛИ пустой объект DataFrame находится в объеме метода, но не для моего случая. Согласно @ комментарий DSM, «но Append не бывает на месте, так что вы должны будете хранить выход, если вы хотите:»

IOW, я должен был бы иметь что-то вроде:

df = df.append(results_df, ignore_index=True) 

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

Есть ли способ сделать это возможным? Это отлично работает с методом python extend для расширения содержимого объекта списка (я понимаю, что DataFrames - это не списки, но ...). Есть ли аналогичный способ сделать это с объектом DataFrame, не имея дело с моими проблемами в области обзора для df?

Btw, метод Pandas concat также работает, но я столкнулся с проблемой переменного объема.

+0

Извините, вы говорите, что 'df = df.append (df_join_out, ignore_index = True)' не работает? – EdChum

+0

'df' с правой стороны затем передается как нерешенная ссылка. –

ответ

3

В Python3, вы можете использовать ключевое слово нелокального:

def outer_method(): 
    ... do outer scope stuff here 
    df = pd.DataFrame(columns=['A','B','C','D']) 
    def recursive_method(arg): 
     nonlocal df 
     ... do local stuff here 
     # func returns a data frame to be appended to empty data frame 
     results_df = func(args) 
     df = df.append(results_df, ignore_index=True) 
     return results 

return df 

Но обратите внимание, что вызов df.append возвращает новый DataFrame каждый раз, и, следовательно, требует копирования все старые данные в новую DataFrame. Если вы делаете это внутри цикла N раз, вы в итоге получаете порядка 1 + 2 + 3 + ... + N = O (N^2) копии - очень плохо для производительности.


Если вам не нужно df внутри recursive_method для любых целей, кроме добавляющих, то лучше добавить в список, а затем построить DataFrame (по телефону pd.concatраз) после того, как recursive_method делается :

df = pd.DataFrame(columns=['A','B','C','D']) 
data = [df] 
def recursive_method(arg, data): 
    ... do stuff here 
    # func returns a data frame to be appended to empty data frame 
    results_df = func(args) 
    data.append(df_join_out) 
    return results 
recursive_method(arg, data) 
df = pd.concat(data, ignore_index=True) 

Это лучшее решение, если все, что вам нужно сделать, это собрать данные внутри recursive_method и может ждать, чтобы построить новый df после recursive_method сделан.


В python2, если вы должны использовать df внутри recursive_method, то вы могли бы пройти df в качестве аргумента recursive_method и вернуть df слишком:

df = pd.DataFrame(columns=['A','B','C','D']) 
def recursive_method(arg, df): 
    ... do stuff here 
    results, df = recursive_method(arg, df) 
    # func returns a data frame to be appended to empty data frame 
    results_df = func(args) 
    df = df.append(results_df, ignore_index=True) 
    return results, df 
results, df = recursive_method(arg, df) 

, но быть в курсе, что вы будете заплатив большую цену за копирование 0 (N^2) , упомянутое выше.


Почему DataFrames не может не должен быть добавлен в месте:

, лежащие в основе данные в DataFrame хранится в NumPy массивов. Данные в массиве NumPy поступают из непрерывного блока памяти. Иногда нет места , чтобы изменить размер массивов NumPy на более крупный смежный блок памяти , даже если память доступна. Представьте, что массив находится между иными структурами данных. В этом случае для изменения размера массива новый более крупный блок памяти должен быть выделен где-то еще, и все данные из исходного массива должны быть скопированы в новый блок. В целом, это не может быть сделано на месте.

DataFrames У меня есть частный метод, _update_inplace, который может быть используется для перенаправления базовых данных DataFrame на новые данные. Это всего лишь pseudo-inplace operation, так как новые данные (считайте массивы NumPy) должны быть , выделенные (со всем сопровождающим копированием). Таким образом, используя _update_inplace, два удара против него: он использует частный метод, который (теоретически) может быть не вокруг в будущих версиях Панд, и он несет штраф за копирование O (N^2).

In [231]: df = pd.DataFrame([[0,1,2]]) 

In [232]: df 
Out[232]: 
    0 1 2 
0 0 1 2 

In [233]: df._update_inplace(df.append([[3,4,5]])) 

In [234]: df 
Out[234]: 
    0 1 2 
0 0 1 2 
0 3 4 5 
+0

Спасибо за объяснение. Это имеет смысл. Я определенно предпочел бы не передавать 'df' в качестве аргумента для моего рекурсивного метода (или использовать ** нелокальное ключевое слово **) по этой точной причине. Кроме того, я ** использовал ** список, но я переключался между списками и кадрами данных, что было дорогостоящим по производительности, поэтому, поскольку я использовал фреймы данных для выполнения заданных операций, я думал, что обойдусь назад и вперед между этими двумя типами объектов до конца, когда я вернул окончательные результаты через JSON. Но, я думаю, ваше предложение использовать список для 'concat' - хороший компромисс. –

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