Створення порожнього пакета даних Pandas, а потім заповнення?


461

Я починаю з документів Pandas 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в пандах копіюється весь набір даних на новий об’єкт ', чи додавання пітонів працює інакше?
Ламма

@Lamma, будь ласка, знайдіть деталі у моїй відповіді нижче. Додаючи до 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 ( NaNі), просто написавши:

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
На мій досвід, створювати кадр даних потрібного розміру, заповненого NaNs, а потім заповнювати значеннями набагато набагато повільніше, ніж створювати кадр даних із розмірами indexx 0( columns = []) та приєднувати по одному стовпчику в кожному витку циклу. Я маю df[col_name] = pandas.Series([...])на увазі в циклі ітерації через назви стовпців. У першому випадку не тільки розподіл пам'яті вимагає часу, але й заміна NaN на нові значення здається надзвичайно повільною.
deeenes

5
@deeenes точно. ця відповідь, ймовірно, повинна зробити це зрозуміліше - ви дуже рідко (якщо і колись) захочете створити порожній фрейм даних (з NaN).
Енді Хайден

1
Згідно з цією відповіддю stackoverflow.com/a/30267881/2302569 Вам потрібно призначити результат повні, або пройти парам на місці = Істинно
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 

У цьому прикладі я використовую цей документ pandas для створення нового фрейму даних, а потім використовую додавання для запису в newDF з даними з oldDF.

Якщо мені доведеться додавати нові дані в цей новийDF з більш ніж одного старого DF, я просто використовую цикл для ітерації через pandas.DataFrame.append ()


14
Зауважте, що append(і аналогічно concat) кожного разу копіює повний набір даних до нового об'єкта, отже, повторення та додавання може і призведе до значного удару щодо продуктивності. Докладніші відомості можна знайти за адресою
MoustafaAAtta

4
@MoustafaAAtta Які існують альтернативи для додавання даних ітераційно до фрейму даних?
MysteryGuy

2
@MoustafaAAtta Чи відповідає Фред у цьому дописі: stackoverflow.com/questions/10715965/… краще на цю точку зору?
MysteryGuy

@MoustafaAAtta, можливо, ви можете додати просто рядки до фрейму даних, він все одно створить новий об’єкт, але для менших наборів даних може бути корисним. pandas.pydata.org/pandas-docs/stable/user_guide/…
geekidharsh

135

Правильний шлях ™ для створення фрейму даних

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їх усім).

Остання перевага полягає в тому, що а RangeIndexавтоматично створюється для ваших даних , тому турбуватися про одну меншу річ (погляньте на бідні 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 може бути обчислювально інтенсивнішим, ніж один конкатенат. Кращим рішенням є додавання цих рядків до списку, а потім об'єднання списку з оригінальним DataFrame всім відразу.

Інша помилка, пов’язана з df.appendтим, що користувачі схильні забувати додавання, не є функцією на місці , тому результат потрібно присвоювати назад. Ви також повинні турбуватися про типи:

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

Робота з стовпцями об'єктів ніколи не є хорошою справою, оскільки панди не можуть векторизувати операції над цими стовпцями. Вам потрібно буде це зробити, щоб виправити це:

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і ще потворніше.

Порожні фрейми даних NaN

Потім створюється 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]

Доказ є в пудингу

Визначення часу цих методів - це найшвидший спосіб зрозуміти, наскільки вони відрізняються за своєю пам'яттю та корисністю.

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

Код бенчмаркінгу для довідки.


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" краща, але вона поступово стає повільнішою, збільшуючи кількість записів у циклі.

Якщо ви плануєте зробити тонкі всередині великого циклу (скажімо, 10M‌ записів або близько того), вам краще використовувати суміш цих двох; заповніть кадр даних 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])

Ви можете замінити перший x in pd.Series([x], index = [x])будь-яким значенням

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.