Создание пустого DataFrame Pandas, а затем его заполнение?

463

Я начинаю с документов панд DataFrame здесь: http://pandas.pydata.org/pandas-docs/stable/dsintro.html

Я хотел бы итеративно заполнять DataFrame значениями для расчета временных рядов. В общем, я бы хотел инициализировать DataFrame столбцами A, B и строками отметок времени, все 0 или все NaN.

Затем я добавляю начальные значения и просматриваю эти данные, вычисляя новую строку из предыдущей строки, скажем row[A][t] = row[A][t-1]+1так или около того.

В настоящее время я использую код, как показано ниже, но я чувствую, что он немного уродлив, и должен быть способ сделать это напрямую с помощью DataFrame, или просто лучше. Примечание: я использую Python 2.7.

import datetime as dt
import pandas as pd
import scipy as s

if __name__ == '__main__':
    base = dt.datetime.today().date()
    dates = [ base - dt.timedelta(days=x) for x in range(0,10) ]
    dates.sort()

    valdict = {}
    symbols = ['A','B', 'C']
    for symb in symbols:
        valdict[symb] = pd.Series( s.zeros( len(dates)), dates )

    for thedate in dates:
        if thedate > dates[0]:
            for symb in valdict:
                valdict[symb][thedate] = 1+valdict[symb][thedate - dt.timedelta(days=1)]

    print valdict
Матиас Кауэр
источник
6
Никогда не увеличивайте DataFrame! Всегда дешевле добавить в список Python и затем в конце преобразовать его в DataFrame, как с точки зрения памяти, так и производительности.
cs95
@ cs95 Что функционально отличается между .appendpd и добавлением списка? Я знаю, что .appendв pandas копирует весь набор данных в новый объект ´, присоединяются ли питоны по-другому?
Ламма
@ Ламма, пожалуйста, найди подробности в моем ответе ниже. При добавлении к df каждый раз в памяти создается новый DataFrame вместо использования существующего, что, откровенно говоря, является пустой тратой.
cs95

Ответы:

330

Вот пара предложений:

Используйте date_rangeдля индекса:

import datetime
import pandas as pd
import numpy as np

todays_date = datetime.datetime.now().date()
index = pd.date_range(todays_date-datetime.timedelta(10), periods=10, freq='D')

columns = ['A','B', 'C']

Примечание: мы могли бы создать пустой DataFrame (с NaNs) просто написав:

df_ = pd.DataFrame(index=index, columns=columns)
df_ = df_.fillna(0) # with 0s rather than NaNs

Чтобы выполнить такой тип вычислений для данных, используйте массив numpy:

data = np.array([np.arange(10)]*3).T

Следовательно, мы можем создать DataFrame:

In [10]: df = pd.DataFrame(data, index=index, columns=columns)

In [11]: df
Out[11]: 
            A  B  C
2012-11-29  0  0  0
2012-11-30  1  1  1
2012-12-01  2  2  2
2012-12-02  3  3  3
2012-12-03  4  4  4
2012-12-04  5  5  5
2012-12-05  6  6  6
2012-12-06  7  7  7
2012-12-07  8  8  8
2012-12-08  9  9  9
Энди Хейден
источник
2
pd.date_range () не работает для меня. Я пытался с DateRange (из автодополнения eclipse), но это работает со строками как формат даты, верно? Общий подход работает, хотя (я изменил индекс на что-то еще).
Матиас Кауэр
2
date_range - это заводская функция для создания индексов даты и времени, которая была новой функцией в 0.8.0 , я бы определенно рекомендовал обновить ее до последней стабильной версии (0.9.1), в которой есть множество исправлений ошибок и новых функций. :)
Энди Хейден
26
По моему опыту, создание фрейма данных необходимого размера, заполненного NaN, а затем заполнение значениями намного-намного медленнее, чем создание фрейма данных с indexx 0измерениями ( columns = []) и присоединение одного столбца за каждый оборот цикла. Я имею df[col_name] = pandas.Series([...])в виду в цикле итерации по именам столбцов. В первом случае не только выделение памяти занимает много времени, но замена NaN новыми значениями кажется чрезвычайно медленной.
Deeenes
5
@deeenes определенно. этот ответ, вероятно, должен прояснить ситуацию - вы очень редко (если вообще) хотите создать пустой Dataframe (из NaN).
Энди Хейден
1
Согласно этому ответу stackoverflow.com/a/30267881/2302569 Вам необходимо присвоить результат fillna или передать параметр на месте = True
JayJay
169

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

newDF = pd.DataFrame() #creates a new dataframe that's empty
newDF = newDF.append(oldDF, ignore_index = True) # ignoring index is optional
# try printing some data from newDF
print newDF.head() #again optional 

В этом примере я использую этот панд документ , чтобы создать новый фрейм данных , а затем с помощью Append команду для записи в newDF с данными из oldDF.

Если мне нужно продолжать добавлять новые данные в этот newDF из более чем одного oldDF, я просто использую цикл for для перебора pandas.DataFrame.append ()

geekidharsh
источник
14
Обратите внимание, что append(и аналогично concat) копирует полный набор данных в новый объект каждый раз, следовательно, повторение и добавление может и приведет к значительному снижению производительности. Для получения дополнительной информации см: pandas.pydata.org/pandas-docs/stable/merging.html
MoustafaAAtta
4
@MoustafaAAtta Каковы альтернативные варианты итеративного добавления данных в фрейм данных?
MysteryGuy
2
@MoustafaAAtta Фред ответит в этом посте: stackoverflow.com/questions/10715965/… лучше с этой точки зрения?
MysteryGuy
@MoustafaAAtta, возможно, вы можете добавить только строки в фрейм данных, это все равно создаст новый объект, но для небольших наборов данных, может быть полезно. pandas.pydata.org/pandas-docs/stable/user_guide/…
geekidharsh
136

Правильный путь ™ для создания DataFrame

TLDR; (просто прочитайте жирный текст)

Большинство ответов здесь расскажут вам, как создать пустой DataFrame и заполнить его, но никто не скажет вам, что это плохо.

Вот мой совет: подождите, пока вы не убедитесь, что у вас есть все данные, с которыми вам нужно работать. Используйте список для сбора ваших данных, затем инициализируйте DataFrame, когда будете готовы.

data = []
for a, b, c in some_function_that_yields_data():
    data.append([a, b, c])

df = pd.DataFrame(data, columns=['A', 'B', 'C'])

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

Другое преимущество этого метода заключается в том, что dtypesони автоматически выводятся (а не присваиваются objectвсем).

Последнее преимущество заключается в том, что для ваших данных автоматически создается aRangeIndex , так что беспокоиться об этом будет меньше (взгляните на плохие методы appendи locметоды ниже, вы увидите элементы в обоих, которые требуют соответствующей обработки индекса).


То, что вы не должны делать

append или concat внутри цикла

Вот самая большая ошибка, которую я видел от новичков:

df = pd.DataFrame(columns=['A', 'B', 'C'])
for a, b, c in some_function_that_yields_data():
    df = df.append({'A': i, 'B': b, 'C': c}, ignore_index=True) # yuck
    # or similarly,
    # df = pd.concat([df, pd.Series({'A': i, 'B': b, 'C': c})], ignore_index=True)

Память перераспределена для каждых appendили concatопераций у вас есть. Соедините это с циклом, и вы получите квадратичную операцию сложности . Со df.appendстраницы документа :

Итеративное добавление строк в DataFrame может быть более сложным в вычислительном отношении, чем одна конкатенация. Лучшее решение - добавить эти строки в список, а затем объединить список с исходным кадром данных сразу.

Другая ошибка, связанная с этим, df.appendзаключается в том, что пользователи, как правило, забывают, что добавление не является функцией на месте , поэтому результат должен быть возвращен обратно. Вы также должны беспокоиться о dtypes:

df = pd.DataFrame(columns=['A', 'B', 'C'])
df = df.append({'A': 1, 'B': 12.3, 'C': 'xyz'}, ignore_index=True)

df.dtypes
A     object   # yuck!
B    float64
C     object
dtype: object

Работа со столбцами объекта никогда не бывает хорошей, потому что pandas не может векторизовать операции над этими столбцами. Вам нужно будет сделать это, чтобы это исправить:

df.infer_objects().dtypes
A      int64
B    float64
C     object
dtype: object

loc внутри петли

Я также видел, как locиспользуется для добавления в DataFrame, который был создан пустым:

df = pd.DataFrame(columns=['A', 'B', 'C'])
for a, b, c in some_function_that_yields_data():
    df.loc[len(df)] = [a, b, c]

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

Пустой фрейм данных NaNs

И затем, создается DataFrame из NaN и все связанные с этим предостережения.

df = pd.DataFrame(columns=['A', 'B', 'C'], index=range(5))
df
     A    B    C
0  NaN  NaN  NaN
1  NaN  NaN  NaN
2  NaN  NaN  NaN
3  NaN  NaN  NaN
4  NaN  NaN  NaN

Он создает DataFrame столбцов объекта, как и другие.

df.dtypes
A    object  # you DON'T want this
B    object
C    object
dtype: object

В приложении есть все вопросы, описанные выше.

for i, (a, b, c) in enumerate(some_function_that_yields_data()):
    df.iloc[i] = [a, b, c]

Доказательство в пудинге

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

введите описание изображения здесь

Контрольный код для справки.

cs95
источник
6
Добавление списка должно быть лучшим способом для этого типа вопроса
YOBEN_S
9
За это нужно проголосовать в миллион раз больше. Никогда не увеличивайте датафрейм!
Багги
3
@ user3293236 Жаль, что начинать надо с нуля каждый раз, когда отвечаешь на старый вопрос;)
cs95
2
Это одна из вещей, которые я ненавижу больше всего. Много раз вы видите 𝒓𝒆𝒂𝒍 𝒄𝒐𝒓𝒓𝒆𝒄𝒕 𝒂𝒏𝒔𝒘𝒆𝒓, который просто остается где-то с несколькими голосами и никогда не принимается. Я скучаю по коду с 𝚙𝚍 = 𝚙𝚍.𝙳𝚊𝚝𝚊𝙵𝚛𝚊𝚖𝚎 ([]), чтобы создать пустой кадр данных для панд. Проголосую за этот ответ. Отличное объяснение, @ cs95!
Ионафан
1
Это буквально в документации. «Итеративное добавление строк в DataFrame может быть более сложным в вычислительном отношении, чем одна конкатенация. Лучшее решение - добавить эти строки в список, а затем объединить список с оригинальным DataFrame сразу». pandas.pydata.org/pandas-docs/version/0.21/generated/...
эндолиты
132

Инициализировать пустой кадр с именами столбцов

import pandas as pd

col_names =  ['A', 'B', 'C']
my_df  = pd.DataFrame(columns = col_names)
my_df

Добавить новую запись в кадр

my_df.loc[len(my_df)] = [2, 4, 5]

Вы также можете передать словарь:

my_dic = {'A':2, 'B':4, 'C':5}
my_df.loc[len(my_df)] = my_dic 

Добавить еще один кадр в ваш существующий кадр

col_names =  ['A', 'B', 'C']
my_df2  = pd.DataFrame(columns = col_names)
my_df = my_df.append(my_df2)

Вопросы производительности

Если вы добавляете строки в цикл, учитывайте проблемы с производительностью. Примерно для первых 1000 записей «my_df.loc» производительность лучше, но постепенно она становится медленнее с увеличением количества записей в цикле.

Если вы планируете делать что-то внутри большого цикла (скажем, 10 миллионов записей или около того), вам лучше использовать смесь этих двух; заполняйте фрейм данных iloc, пока размер не достигнет 1000, затем добавьте его к исходному фрейму данных и очистите временный фрейм данных. Это повысит вашу производительность примерно в 10 раз.

Афшин Амири
источник
my_df = my_df.append(my_df2)у меня не работает, если я не укажу ignore_index=True.
Насиф Имтиаз Охи
0

Предположим, датафрейм с 19 строками

index=range(0,19)
index

columns=['A']
test = pd.DataFrame(index=index, columns=columns)

Сохранение столбца А в качестве константы

test['A']=10

Сохранение столбца b как переменной, заданной циклом

for x in range(0,19):
    test.loc[[x], 'b'] = pd.Series([x], index = [x])

Вы можете заменить первый х в pd.Series([x], index = [x])с любым значением

Аджай Охри
источник