Правильний шлях ™ для створення фрейму даних
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]
Доказ є в пудингу
Визначення часу цих методів - це найшвидший спосіб зрозуміти, наскільки вони відрізняються за своєю пам'яттю та корисністю.
Код бенчмаркінгу для довідки.