Циклический просмотр кадра данных pandas с 15000 записями очень медленный, занимает 72 секунды

У меня есть pandas DataFrame, содержащий 15000 записей и 20 столбцов, прочитанных из файла Excel. С использованием этого кода чтение файла Excel в DataFrame занимает около 4,13 секунды. Версия pandas в моей системе — 2.0.2.

df = pd.read_excel(excel_path, sheet_name='Sheet 1', header=[
                         2, 3]).astype(object).replace(np.nan, 'None')

Я перебираю DataFrame, используя цикл for поверх iloc и создаю словарь с именами столбцов в качестве ключей словаря, но с разными именами. Например:

data = []
for i in df.iloc:
    mydict = {}
    mydict['col1'] = i['Column 1 Name'].values[0]
    mydict['col2'] = i['Column 2 Name'].values[0]

    mydict['doc_date'] = datetime.datetime.strftime(i['Doc Details']['Doc Date'], r'%d-%m-%Y') \
        if isinstance(i['Doc Details']['Doc Date'], datetime.datetime) \
        else i['Doc Details']['Doc Date'].replace('/', '-')


    # 17 more columns

    data.append(mydict)

Цикл for занимает около 72 секунд.

Каков более быстрый способ зацикливания DataFrame и создания словаря? Цикл for не обрабатывает ни один из столбцов, кроме изменения имен ключей для словаря и использования условия if для чтения столбца даты и времени.

Почему цикл for должен занимать 72 секунды, когда такое же количество записей читается библиотекой pandas всего за 4 секунды?

РЕДАКТИРОВАТЬ 1:

Требуемый результат или преобразование — это список объектов словаря. Каждый объект словаря будет иметь пары ключ: значение для всех столбцов одной строки. В списке будет столько объектов словаря, сколько строк.

Обновлено еще раз:

Если мой Excel такой:

Col 1   Col B   Col C
0       0       0
1       1       1
2       2       2
3       3       3
4       4       4

Мне нужен такой вывод:

[
    {'mycol1': '0', 'mycol2': '0', 'mycol3': '0'
    },
    {'mycol1': '1', 'mycol2': '1', 'mycol3': '1'
    },
    {'mycol1': '2', 'mycol2': '2', 'mycol3': '2'
    },
    {'mycol1': '3', 'mycol2': '3', 'mycol3': '3'
    },
    {'mycol1': '4', 'mycol2': '4', 'mycol3': '4'
    }
]

Обратите внимание, что каждый объект словаря имеет ключи столбцов, но с именами, отличными от имен столбцов в Excel.

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

Почему mydict = {} находится в петле? Вы уверены?

Anastasiya-Romanova 秀 07.06.2023 09:41

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

AllSolutions 07.06.2023 09:44

Вы пробовали df.to_dict('records')?

Corralien 07.06.2023 10:06
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
3
113
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Пытаться:

df.set_index('col1').to_dict('dict')

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

AllSolutions 07.06.2023 09:38

Попробуй это:

df=pd.DataFrame({'A':np.arange(5),'B':np.arange(5),'C':np.arange(5)})
print(df)

    A   B   C
0   0   0   0
1   1   1   1
2   2   2   2
3   3   3   3
4   4   4   4

# Transpose the dataframe then create 
list(df.T.to_dict().values())

[{'A': 0, 'B': 0, 'C': 0},
 {'A': 1, 'B': 1, 'C': 1},
 {'A': 2, 'B': 2, 'C': 2},
 {'A': 3, 'B': 3, 'C': 3},
 {'A': 4, 'B': 4, 'C': 4}]

Нет, я пытаюсь сделать это: [{'A': '0', 'B': '0', 'C': '0'}, {'A': '1', 'B': ' 1', 'C': '1'}, {'A': '2', 'B': '2', 'C': '2'}, {'A': '3', 'B' : '3', 'C': '3'}, {'A': '4', 'B': '4', 'C': '4'}]

AllSolutions 07.06.2023 09:50

Использование простых циклов for в python для обработки больших данных никогда не бывает хорошей идеей. Цикл for имеет много накладных расходов, которые замедляют обработку на несколько порядков.

Чтобы преобразовать DataFrame в dict, используйте встроенную функцию pandas to_dict(), что займет пару миллисекунд.

Выполните любые другие преобразования (например, обработку даты и времени) в DataFrame заранее. Преобразование в dict должно быть последним шагом.

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

d = {"mycol1":[0,1,2], "mycol2":[3,4,5]}
r = []
for i in range(len(list(d.values())[0])):
    r.append(dict(zip(
        list(d.keys()), 
        [a[i] for a in list(d.values())])
    ))
# returns [{'mycol1': 0, 'mycol2': 3}, {'mycol1': 1, 'mycol2': 4}, {'mycol1': 2, 'mycol2': 5}]

Это очень грязно, потому что dicts не созданы для такой манипуляции с данными, но это работает.

хорошо, но как мне получить список объектов словаря, как объяснено в моем отредактированном вопросе.

AllSolutions 07.06.2023 10:00

проверьте мой отредактированный ответ, решает ли это вашу проблему?

Quantum 07.06.2023 10:17

Основываясь на приведенном примере, возможно, вы можете попробовать использовать понимание списка. Что-то вроде:

[{'mycol1': str(val1), 'mycol2': str(val2), 'mycol3': str(val3)} for val1, val2, val3 in zip(df['Col 1'], df['Col B'], df['Col C'])]

Это тоже работает, но, узнав о df.to_dict('records'), я думаю, что это лучший подход для моих текущих требований.

AllSolutions 07.06.2023 10:55
Ответ принят как подходящий

Попробуйте df.to_dict('records'):

dmap = {'A': 'mycol1', 'B': 'mycol2', 'C': 'mycol3'}
out = df.rename(columns=dmap).to_dict('records')

Выход:

>>> out
[{'mycol1': 0, 'mycol2': 0, 'mycol3': 0},
 {'mycol1': 1, 'mycol2': 1, 'mycol3': 1},
 {'mycol1': 2, 'mycol2': 2, 'mycol3': 2},
 {'mycol1': 3, 'mycol2': 3, 'mycol3': 3},
 {'mycol1': 4, 'mycol2': 4, 'mycol3': 4}]

Спасибо. Это довольно аккуратный и чистый код. В моем фактическом коде, который я разместил в своем вопросе, у меня есть 2 строки заголовка - строки 2 и 3. Каким-то образом переименование не работает с использованием df.rename, если я передаю такой словарь: dmap = {'A': 'mycol1', 'B': 'mycol2', 'C': 'mycol3'} За исключением переименования столбца, вывод в желаемой структуре списка объектов словаря

AllSolutions 07.06.2023 10:42

Я опубликую отдельный вопрос о переименовании столбцов, когда файл Excel имеет несколько столбцов заголовков, и размещу ссылку на этот вопрос здесь. Спасибо!

AllSolutions 07.06.2023 10:57

@AllSolutions До чего это векторизованное решение сокращает время выполнения?

Pep_8_Guardiola 07.06.2023 11:44

Я сравнил слегка модифицированную версию решения Химаншу Панвара (чтобы соответствовать тому, что было задано в вопросе ОП), с использованием понимания списка (на основе решения Анастасии-Романовой ) и решения Корральена:

import pandas as pd
import numpy as np
from time import perf_counter

df = pd.DataFrame({
    'Col A': np.arange(5000),
    'Col B': np.arange(5000),
    'Col C': np.arange(5000),
    })
    
print(df)

dmap = {'Col A': 'mycol1', 'Col B': 'mycol2', 'Col C': 'mycol3'}
new_names = ['mycol1', 'mycol2', 'mycol3']

#Himanshu Panwar's solution
t_start = perf_counter()
new_df1 = list(df.rename(columns=dmap).T.to_dict().values())
t_end = perf_counter()
Himanshu_Panwar_time = t_end-t_start


#List comprehension (Based on Anastasiya-Romanova's solution)
perf_counter()
new_df2 = [{new_names[ii]: value for ii, value in enumerate(row)} for row in zip(df['Col A'], df['Col B'], df['Col C'])]
t_end = perf_counter()
List_comprehension_time = t_end-t_start

#Corralien's solution
perf_counter()
new_df3 = df.rename(columns=dmap).to_dict('records')
t_end = perf_counter()
Corralien_time = t_end-t_start

print("\n", new_df1==new_df2==new_df3)
print("Himanshu Panwar's solution :", Himanshu_Panwar_time)
print("List comprehension (Based on Anastasiya-Romanova's solution) :", List_comprehension_time)
print("Corralien's solution :", Corralien_time)

Выходы:

      Col A  Col B  Col C
0         0      0      0
1         1      1      1
2         2      2      2
3         3      3      3
4         4      4      4
...     ...    ...    ...
4995   4995   4995   4995
4996   4996   4996   4996
4997   4997   4997   4997
4998   4998   4998   4998
4999   4999   4999   4999

[5000 rows x 3 columns]

 True
Himanshu Panwar's solution : 0.3294021999463439
List comprehension (Based on Anastasiya-Romanova's solution) : 0.33864239999093115
Corralien's solution : 0.36161809996701777

Интересно, но самое быстрое решение не дает ожидаемого результата, а второе самое быстрое решение - больно писать на 20 столбцов (но это можно улучшить) :)

Corralien 07.06.2023 11:22

@Corralien Корральен Ты прав, я отредактировал свой ответ.

diogeek 07.06.2023 11:53

Другие вопросы по теме