Примените пользовательскую функцию к нескольким строкам фрейма данных

#python #pandas #dataframe

Вопрос:

В настоящее время я работаю над функцией, которая определит, является ли строка дубликатом на основе нескольких условий (квадратные метры, изображения и цена). Он работает отлично, пока не найдет дубликат, не удалит строку из фрейма данных, а затем мой цикл for будет нарушен. Это производит IndexError: single positional indexer is out-of-bounds .

 def image_duplicate(df):
    # Detecting duplicates based on the publications' images, m2 and price.
    for index1 in df.index:
        for index2 in df.index:
            if index1 == index2:
                continue
            print('index1: {} t index2: {}'.format(index1, index2))

            img1 = Image.open(requests.get(df['img_url'].iloc[index1], stream=True).raw).resize((213, 160))
            img2 = Image.open(requests.get(df['img_url'].iloc[index2], stream=True).raw).resize((213, 160))
            img1 = np.array(img1).astype(float)
            img2 = np.array(img2).astype(float)

            ssim_result = ssim(img1, img2, multichannel=True)

            ssim_result_percentage = (1 ssim_result)/2

            if ssim_result_percentage > 0.80 and df['m2'].iloc[index1] == df['m2'].iloc[index2] 
                    and df['Price'].iloc[index1] == df['Price'].iloc[index2]:
                df.drop(df.iloc[index2], inplace=True).reindex()


image_duplicate(full_df)
 

Что было бы хорошим решением этой проблемы?

ИЗМЕНИТЬ: Пример: введите описание изображения здесь

Ожидаемый результат: Удалите строку с одной спальней [2] из фрейма данных.

Комментарии:

1. Всякий раз, когда это возможно, вы не должны зацикливаться на фрейме данных, вы, вероятно, можете использовать apply метод или векторные операции. Здесь вы меняете фрейм данных во время цикла, хотя это не может работать. Пожалуйста, приведите пример ваших данных и ожидаемый результат.

2. Есть ли способ использовать две строки или все строки фрейма данных при сравнении с одной? Построил образец, пожалуйста, дайте мне знать, если этого достаточно @mozway

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

4. Итак, здесь вы хотите сравнить все изображения в комбинациях строк? Тогда я предлагаю вам сначала создать функцию, которая принимает два изображения (или имени файлов) и возвращает True/False, если они достаточно похожи similar(img1, img2) -> True/False . Тогда вы сможете применять его более легко

5. @CaptainCsaba Проблема в том, что у меня будет многократный ввод, так как код многократно повторяет отверстие df.

Ответ №1:

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

Я бы рекомендовал что-то подобное, чтобы решить вашу проблему:

 import itertools

def image_duplicate(df):
    # Detecting duplicates based on the publications' images, m2 and price.
    indexes_to_drop = []
    for index1, index2 in itertools.product(df.index, df.index):
        if index1 == index2:
            continue
        print("index1: {} t index2: {}".format(index1, index2))

        img1 = Image.open(requests.get(df["img_url"].iloc[index1], stream=True).raw).resize((213, 160))
        img2 = Image.open(requests.get(df["img_url"].iloc[index2], stream=True).raw).resize((213, 160))
        img1 = np.array(img1).astype(float)
        img2 = np.array(img2).astype(float)

        ssim_result = ssim(img1, img2, multichannel=True)
        ssim_result_percentage = (1   ssim_result) / 2

        if (
            ssim_result_percentage > 0.80
            and df["m2"].iloc[index1] == df["m2"].iloc[index2]
            and df["Price"].iloc[index1] == df["Price"].iloc[index2]
        ):
            indexes_to_drop.append(index2)

    indexes_to_drop = list(set(indexes_to_drop))
    return df.drop(indexes_to_remove)


output_df = image_duplicate(full_df)  # `output_df` should contain the expected output
 

Объяснение:

  1. Перебирайте индексы (в таких случаях я предпочитаю использовать itertools, но не стесняйтесь использовать свой подход с циклами for)
  2. Создайте indexes_to_drop список и вместо удаления в конце добавьте эти индексы в список
  3. Получить уникальный список индексов для удаления (это может произойти, тогда идентичный индекс будет присутствовать в списке несколько раз) — list(set(indexes_to_drop)) это простой способ удаления дубликатов (набор не может содержать дубликатов)
  4. Отбросьте эти индексы сразу (не уверен, почему вы использовали .reindex их в своем примере)

Могут быть и другие способы улучшения вашего кода, например, не сравнивайте изображения там, где index2 они уже есть в indexes_to_drop списке (например, проверьте if index2 in indexes_to_drop и продолжите, если это правда), или вы даже можете превратить это в функцию, с которой можно было бы использовать apply (повторение index2 произойдет внутри apply), но в этом нет необходимости.

Комментарии:

1. Эй, были каникулы без доступа в Интернет, следовательно, ответа не было. Я действительно смог решить проблему с помощью метода «попробуй и исключи» и кэширования изображений, так как столбец становился слишком большим. Ваш путь на самом деле намного чище!