Векторизованная пользовательская функция не работает должным образом в pandas - PullRequest
2 голосов
/ 16 июня 2020

С this pycon talk в качестве источника.

def clean_string(item):
    if type(item)==type(1):
        return item
    else:
        return np.nan

объект dataframe имеет столбец, содержащий числовые и строковые данные, я хочу изменить строки на np.nan, оставив числовые данные как есть.

Этот подход работает нормально

df['Energy Supply'].apply(clean_string)

, но когда я пытаюсь использовать векторизацию, значения всех элементов столбца меняются на np.nan

df['Energy Supply'] = clean_string(df['Energy Supply'])  # vectorisation

, но вышеуказанный метод преобразует все элементы в np.nan. Я считаю, что это потому, что функция type(item) в clean_string имеет тип pd.Series.

Есть ли способ решить эту проблему?

PS: Я новичок в pandas

1 Ответ

1 голос
/ 16 июня 2020

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

Причина, по которой ваш код не Во втором случае не работает то, что вы передаете всю серию своей функции clean_string(). Он сравнивает тип Серии с type(1), который равен False, а затем возвращает одно значение np.nan. Pandas автоматически передает это значение при назначении его обратно в df, поэтому вы получаете столбец NaN. Чтобы избежать этого, вам придется l oop по всем элементам в Series в вашей функции clean_string().

Из любопытства я попробовал несколько других подходов, чтобы увидеть, есть ли они будут быстрее вашей версии. Для тестирования я создал 10 000 и 100 000 элементов pd.Series с чередующимися целочисленными и строковыми значениями:

import numpy as np
import pandas as pd

s = pd.Series(i if i%2==0 else str(i) for i in range(10000))
s2 = pd.Series(i if i%2==0 else str(i) for i in range(100000))

Эти тесты выполняются с использованием pandas 1.0.3 и python 3.8.

Базовый уровень с использованием clean_string()

In []: %timeit s.apply(clean_string)
3.75 ms ± 14.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In []: %timeit s2.apply(clean_string)
39.5 ms ± 301 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

Series.str методов

Альтернативным способом проверки наличия строк и не строк было бы использование встроенных функций .str на например, для серии, если вы примените .str.len(), она вернет NaN для любых нестроковых элементов в серии. В документации по pandas они даже называются « векторизованные строковые методы », поэтому, возможно, они будут более эффективными.

In []: %timeit s.mask(s.str.len()>0)
6 ms ± 39.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In []: %timeit s2.mask(s2.str.len()>0)
56.8 ms ± 142 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

К сожалению, этот подход медленнее, чем .apply(). Несмотря на «векторизацию», похоже, что это лучший подход. Он также не совсем идентичен logi c of clean_string(), потому что он проверяет элементы, которые являются строками, а не элементы, которые являются целыми числами.

Применение type непосредственно к Series

На основе этого ответа я решил протестировать, используя .apply() с type, чтобы получить тип каждого элемента. Как только мы узнаем тип, сравните с int и используйте метод .mask() для преобразования любых нецелых чисел в NaN.

In []: %timeit s.mask(s.apply(type)!=int)
1.88 ms ± 4.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In []: %timeit s2.mask(s2.apply(type)!=int)
15.2 ms ± 32.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Это самый быстрый подход, который я нашел.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...