Присоединяйтесь к сопоставлению Pandas DataFrames по строке и подстроке

Я хочу объединить два фрейма данных путем частичного совпадения строк. Мне нужно объединить два фрейма данных. Первый df1 состоит из 130 000 строк, например:

id    text                        xc1       xc2
1     adidas men shoes            52465     220
2     vakko men suits             49220     224
3     burberry men shirt          78248     289
4     prada women shoes           45780     789
5     lcwaikiki men sunglasses    34788     745

а второй df2 состоит из 8000 строк, например:

id    keyword               abc1     abc2
1     men shoes             1000     11
2     men suits             2000     12
3     men shirt             3000     13
4     women socks           4000     14
5     men sunglasses        5000     15

После соответствия между ключевым словом и текстом результат должен выглядеть следующим образом:

id    text                        xc1       xc2   keyword         abc1  abc2
1     adidas men shoes            52465     220   men shoes       1000  11
2     vakko men suits             49220     224   men suits       2000  12
3     burberry men shirt          78248     289   men shirt       3000  13
4     lcwaikiki men sunglasses    34788     745   men sunglasses  5000  15

Возможно ли, что частичное совпадение строки находится в середине текста в df1? например vakko men suits blue?   —  person muratmert41    schedule 23.05.2021

@SeaBean Да. Когда я исследую набор данных, есть такие примеры.   —  person muratmert41    schedule 23.05.2021

Идентификаторы в том же порядке?   —  person muratmert41    schedule 23.05.2021

@AnuragDabas, к сожалению, нет. номера строк в обоих фреймах данных различаются   —  person muratmert41    schedule 23.05.2021

stackoverflow.com/a/60908516/14066512   —  person muratmert41    schedule 23.05.2021

Еще одна сложность: men suits в df2 не должен совпадать с abc women suits в df1, верно?   —  person muratmert41    schedule 23.05.2021

@SeaBean, да, я бы не хотел, чтобы это совпадало.   —  person muratmert41    schedule 24.05.2021

См. также:  Airflow ExternalTaskSensor с различным интервалом планировщика

В таком случае вы не можете использовать большинство похожих ответов в StackOverflows, которые используют простой тест подстроки, например stringA in stringB. Такой вид теста завершится неудачно с ложным совпадением men suits в keyword с women suits в text, поскольку он возвращает True для теста 'men suits' in 'women suits'. Мы должны использовать регулярное выражение с учетом границы слова, чтобы избежать ложного совпадения.   —  person muratmert41    schedule 24.05.2021

Понравилась статья? Поделиться с друзьями:
IT Шеф
Комментарии: 2
  1. muratmert41

    Давайте подойдем к перекрестному соединению двух фреймов данных, а затем отфильтруем их, сопоставив строку с подстрокой, как показано ниже:

    df3 = df1.merge(df2, how='cross')    # for Pandas version >= 1.2.0 (released in Dec 2020)
    
    import re
    mask = df3.apply(lambda x: (re.search(rf"\b{x['keyword']}\b", str(x['text']))) != None, axis=1)
    df_out = df3.loc[mask]
    

    Если ваша версия Pandas старше 1.2.0 (выпущена в декабре 2020 г.) и не поддерживает слияние с how='cross', вы можете заменить оператор слияния на:

    # For Pandas version < 1.2.0
    df3 = df1.assign(key=1).merge(df2.assign(key=1), on='key').drop('key', axis=1)   
    

    После перекрестного соединения мы создали логическую маску для фильтрации случаев, когда keyword находится в пределах text, используя re.search в .apply().

    Мы должны использовать re.search вместо простого теста подстроки Python, такого как stringA in stringB, который можно найти в большинстве похожих ответов в StackOverflow. Такой тест завершится неудачно с ложным совпадением 'men suits' в keyword с 'women suits' в text, поскольку он возвращает True для теста 'men suits' in 'women suits'.

    Мы используем регулярное выражение с парой метасимволов границы слова \b вокруг keyword (шаблон регулярного выражения: rf"\b{x['keyword']}\b"), чтобы гарантировать соответствие только для совпадения всего слова для text в df1, т.е. men suits в df2 не будет совпадать с women suits в df1, поскольку слово women не имеет границы между буквами wo и men.

    Результат:

    print(df_out)
    
    
        id_x                      text    xc1  xc2  id_y         keyword  abc1  abc2
    0      1          adidas men shoes  52465  220     1       men shoes  1000    11
    6      2           vakko men suits  49220  224     2       men suits  2000    12
    12     3        burberry men shirt  78248  289     3       men shirt  3000    13
    24     5  lcwaikiki men sunglasses  34788  745     5  men sunglasses  5000    15
    

    Здесь столбцы id_x и id_y — это исходный id столбец в df1 и df2 соответственно. Как видно из комментария, это просто номера строк фреймов данных, которые могут вас не беспокоить. Затем мы можем удалить эти 2 столбца и сбросить индекс, чтобы очистить макет:

    df_out = df_out.drop(['id_x', 'id_y'], axis=1).reset_index(drop=True)
    

    Окончательный результат

    print(df_out)
    
    
                           text    xc1  xc2         keyword  abc1  abc2
    0          adidas men shoes  52465  220       men shoes  1000    11
    1           vakko men suits  49220  224       men suits  2000    12
    2        burberry men shirt  78248  289       men shirt  3000    13
    3  lcwaikiki men sunglasses  34788  745  men sunglasses  5000    15
    

    Спасибо за твой ответ. Я получаю следующее сообщение об ошибке: TypeError: expected string or bytes-like object Может ли какое-то значение быть целым числом? person muratmert41; 24.05.2021

    @ muratmert41 В таком случае сначала преобразуйте x['text'] в строку, используя str(x['text']). Вы можете обратиться к моим отредактированным кодам выше. person muratmert41; 24.05.2021

    @ muratmert41 Если я могу еще чем-нибудь помочь, дайте мне знать! :-) person muratmert41; 25.05.2021

    твои ответы действительно мне очень помогают. Я больше не получаю сообщение об ошибке. но есть некоторые мелкие проблемы, которые мешают мне получить правильный результат. Я над ними работаю. person muratmert41; 25.05.2021

    @ muratmert41 Посмотрим, могу ли я помочь вам, если это связано с вашим вопросом здесь. Если нет, надеюсь, вы скоро решите эту проблему! В любом случае, не забудьте принять решение и проголосовать за него! Удачи! person muratmert41; 25.05.2021

    Мне жаль, что я забыл об этом. еще раз спасибо. person muratmert41; 25.05.2021

    @ muratmert41 Добро пожаловать! Удачи в скорейшем решении ваших мелких проблем! person muratmert41; 25.05.2021

  2. muratmert41

    Давайте начнем с того, что отсортируем ключевые слова в порядке убывания длины, чтобы женские костюмы соответствовали мужской одежде.

    lkeys = df2.keyword.reindex(df2.keyword.str.len().sort_values(ascending=False).index)
    

    Теперь определите функцию сопоставления; каждое text значение из df1 будет передано как s для поиска подходящего ключевого слова:

    def is_match(arr, s):
        for a in arr:
            if a in s:
                return a
        return None
    

    Теперь мы можем извлечь ключевое слово из каждого text в df1 и добавить его в новый столбец:

    df1['keyword'] = df1['text'].apply(lambda x: is_match(lkeys, x))
    

    Теперь у нас есть все, что нужно для стандартного слияния:

    pd.merge(df1, df2, on='keyword')
    

    Спасибо за твой ответ. Я предполагаю, что некоторые значения содержат значения типа int. например, например: обод велосипеда 16 дюймов. поэтому я получаю такую ​​ошибку: TypeError: argument of type 'int' is not iterable person muratmert41; 24.05.2021

Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: