Сохранение Pandas DataFrame в середине цикла применения

При работе с пандами необходимо использовать функцию apply или map. В моем случае мне предстоит очень долгая (14 часов) обработка моих данных, и я хочу сохранить DataFrame, если в середине возникнет какая-либо ошибка.

Чтобы конкретизировать режим, рассмотрим следующий код

import pandas as pd
from math import log

data = pd.DataFrame()
data['x'] = [1,2,-1,4,5]

try:
    data['y'] = data['x'].apply(log)
except Exception as e:
    data.to_pickle('data.pkl')
    raise(e)

Когда он выполняется, он вызывает ValueException при попытке вычислить log(-1). Я хотел бы сохранить уже вычисленные данные и повторно вызвать исключение. К сожалению, это не работает. Когда ты попробуешь

data_bis = pd.read_pickle('data.pkl')
print(data_bis)

Вы получаете только столбец x. Насколько я понимаю, сначала панды создают новую серию с вычисленными значениями, а затем добавляют ее в фрейм данных.

Есть ли у вас идеи, как сохранить уже вычисленные данные перед повторным вызовом исключения?

возможно, лучше создать функцию, которая запускается log в try/except, чтобы поймать ошибку.

furas 29.04.2024 18:23

вам придется запускать log для каждой строки отдельно и записывать все данные после каждой строки (или добавлять новую строку в CSV). Но в этом нет смысла — лучше используйте log в try/except

furas 29.04.2024 18:25

@furas да, это одно рабочее решение, но я нашел его «не пандатическим». Известно, что применять и отображать быстрее, чем перебирать строки

EtienneBonnafoux 29.04.2024 18:27

так почему бы не написать функцию def my_log(x): try: return log(x) except ValueError:: return None и позже использовать apply(my_log)

furas 29.04.2024 18:28

Кстати: вам следует проверить numpy.log, потому что, вероятно, он возвращает NaN для -1 вместо того, чтобы выдавать ошибку.

furas 29.04.2024 18:29

Чтобы внести ясность: я не хочу вычислять какой-то журнал, моя функция намного сложнее, и мой DataFrame имеет 100 тысяч строк. Это игрушечный пример

EtienneBonnafoux 29.04.2024 18:34

Таким образом, Exception может быть чем угодно, а не только ValueError.

EtienneBonnafoux 29.04.2024 18:35

Решение может быть тем же: запустить код в try/кроме, чтобы перехватить его.

furas 29.04.2024 18:39
data['x'].apply(log) обрабатывает весь столбец x, прежде чем он будет назначен новому столбцу y. Таким образом, ошибка неизбежно возникнет и y не будет создана. Это не построчная операция x,y.
user19077881 29.04.2024 18:40

Кстати: в apply() вы также можете запустить код, который выполняет ваши вычисления и записывает результат в какой-то другой файл. Но в этой функции также необходимо использовать try/except.

furas 29.04.2024 18:44

@furas проблема не в том, чтобы перехватить исключение, а в том, чтобы сохранить вычисленные данные.

EtienneBonnafoux 29.04.2024 18:47

Я думаю, что лучше перехватывать исключения, а не записывать данные. А если вам нужно это написать, то вы можете сделать это внутри apply() — что-то вроде нового кода в моем ответе.

furas 29.04.2024 18:50

@user19077881 user19077881 Я не хочу, потому что через 14 часов подайте заявку, если возникнет проблема в 13-м часу, это действительно позор.

EtienneBonnafoux 29.04.2024 19:29

@ user19077881Так что я не могу с этим ни жить, ни двигаться дальше.

EtienneBonnafoux 29.04.2024 19:31

Математическое применение функции к столбцу DF со 100 тысячами строк должно занимать менее 0,05 секунды. Очень сложно представить, какую функцию обработки вы используете, которая занимает 14 часов и почему ее нельзя радикально оптимизировать.

user19077881 29.04.2024 21:10

@user19077881 user19077881 представьте, что функция принимает приглашение в качестве аргумента и использует сложный конвейер с некоторыми LLM...

EtienneBonnafoux 29.04.2024 21:11

3-4 второго ряда, 50 тыс. рядов, я позволю вам разобраться

EtienneBonnafoux 29.04.2024 21:12

Я никогда не говорю, что это была математическая функция, журнал был игрушечным примером

EtienneBonnafoux 29.04.2024 21:12
Почему в 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
18
53
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Обновлено:

Если у вас есть другая функция с разными Exceptions, то решение может быть таким же — запустить код в try/except, чтобы перехватить его, и заменить результат на None, numpy.NaN или другое значение.

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

Вы также можете записывать данные в эту функцию и добавлять новый результат к предыдущим результатам.

def my_log(x):
    try:
        result = math.log(x)
        with open('column_x.csv', 'a') as f:
             f.write( f"{result}\n" )
        return result
    except ValueError:
        return None    

Я бы предпочел использовать math.log самостоятельно try/except, чтобы выявить проблему и отправить None

def my_log(x):
    try:
        return log(x)
    except ValueError:
        return None    

data['y'] = data['x'].apply(my_log)

Или я бы использовал numpy.log(), потому что он возвращает NaN вместо ошибки удаления:

import numpy as np

data['z'] = data['x'].apply(np.log)

Полный рабочий пример:

import pandas as pd
import math
import numpy as np

def my_log(x):
    try:
        return math.log(x)
    except ValueError:
        return None    

data = pd.DataFrame()
data['x'] = [1,2,-1,4,5]
    
data['y'] = data['x'].apply(my_log)
data['z'] = data['x'].apply(np.log)

print(data)

Результат:

   x         y         z
0  1  0.000000  0.000000
1  2  0.693147  0.693147
2 -1       NaN       NaN
3  4  1.386294  1.386294
4  5  1.609438  1.609438

Мне не интересно вычислять журнал, это игрушечный пример. Измените «log» на «foo()», который может быть чем угодно.

EtienneBonnafoux 29.04.2024 18:46

И я хочу быть исчерпывающим, перехватывать каждое исключение, а не писать просто «Нэн», повторно вызывать его.

EtienneBonnafoux 29.04.2024 18:48

Я думаю, ваше редактирование 2 наиболее близко к тому, что можно сделать. Спасибо за предложение

EtienneBonnafoux 29.04.2024 18:54

Отвечая на мой собственный вопрос.

Немного похоже на предложение @furas, но без записи каждого шага.

import pandas as pd
from math import log

data = pd.DataFrame()
data['x'] = [1,2,3,4,5,-1,4,5]

def my_log(x, t : list):
    z=log(x)
    t.append(z)
    return z

t = []
try:
    data['y'] = data['x'].apply(my_log,t=t)
except Exception as e:
    t_bis = t+['nan' for _ in range(len(data)-len(t))]
    data['y'] = t_bis
    data.to_pickle('data.pkl')
    raise(e)

Идея состоит в том, чтобы сохранить промежуточное значение в списке для создания полного dataFrame в случае возникновения исключения.

если это решит вашу проблему, вы можете пометить свой ответ как принятый :) А позже вы сможете проголосовать за свой ответ.

furas 29.04.2024 22:01

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

EtienneBonnafoux 29.04.2024 22:03

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