优化此 pandas 转换

Optimizing this pandas transformation

首先,我对pandas完全陌生,所以我不确定这是否叫“转型”,但我已经这样做了,感觉必须有一种更有效的方法(在 LOC 或 运行 时间内)来实现相同的目标。这是我写的:

def findInCol(row, col, tags):
    try:
        for m in validTags:
            if m.lower() in row[col].lower():
                tags.add(m)
            else:
                m_fix = pat.sub('', m.lower())
                row_fix = pat.sub('', row[col].lower())
                if m_fix in row_fix:
                    tags.add(f"{m}")
    except KeyError:
        pass
def getTags(row):
    tags = set()
    findInCol(row, 'Ad_data', tags)
    findInCol(row, 'Name', tags)
    tags = clean(tags)
    return ','.join(tags) if tags else "NA"

def clean(tags):
    arr = list(tags)
    remove = set()
    for i in range(len(arr)):
        for j in range(i+1, len(arr)):
            i_l = pat.sub('', arr[i].lower())
            j_l = pat.sub('', arr[j].lower())
            if i_l in j_l:
                remove.add(i)
            elif j_l in i_l:
                remove.add(j)
    arr = [i for j, i in enumerate(arr) if j not in remove]
    return arr

df['Tag'] = df.fillna("NA").apply(getTags, axis=1)

如果有任何需要澄清的地方,请询问。 这本质上是试图在字段 'Ad_data' y 'Name' 中找到一些有效标签。很多时候它会找到不止一个匹配的标签,这很好。但是,可能 'Horse' 和 'RedHorse' 都是有效标签,所以在我进行初始搜索后,我需要清理以仅保留更具体的标签(即 RedHorse)这在clean函数中执行。

编辑:

这是一个示例数据集。因此,从仅包含名称的 table 开始(暂时忽略 Ad_data,因为在某些情况下它可能不存在),以及在 python 代码中定义的单独的有效标签列表(例如,在这种情况下只是 ["Horse", "RedHorse"]),我需要 获得以下输出 table(标签添加为一列):

我会从 validTags 开始,它是一个单词序列(列表、系列等),但重要的是,这些单词是从最不具体到最具体的(至少在每组可以应用的标签中)到单行)。

>>> validTags = pd.Series(['Horse', 'RedHorse'])
>>> validTags
0       Horse
1    RedHorse
dtype: object

现在您可以从 Ad_dataName 列中提取这些标签:

>>> df = pd.DataFrame({'Name': ['this row is a horse'], 'Ad_data': ['buy a nice REDHORSE']}, index=['a'])
>>> regex = '(' + '|'.join(validTags) + ')'
>>> import re
>>> df['Name'].str.extractall(regex, flags=re.IGNORECASE)[0]
   match
a  0        horse
Name: 0, dtype: object
>>> df['Ad_data'].str.extractall(regex, flags=re.IGNORECASE)[0]
   match
a  0        REDHORSE
Name: 0, dtype: object

您可以看到 extractall returns 所有匹配项的第一个索引级别对应于找到标签的行,第二个索引级别只是匹配项的编号。

我在您的数据中看到一些意想不到的字符,我认为它们是正则表达式 pat。 您可以通过将 df[col].str.extractall(...) 替换为 df[col].str.replace(pat, '').str.extractall(...).

来做到这一点

让我们将所有这些匹配放在一起,然后使用 GroupBy.unique() 提取唯一元素:

>>> allmatches = pd.concat([
...   df[col].str.replace(pat, '').str.extractall(regex, flags=re.IGNORECASE)[0]
... for col in ['Name', 'Ad_data']])
>>> tags = allmatches.str.lower().groupby(level=0).unique()
>>> tags
a    [horse, redhorse]
Name: 0, dtype: object

现在您有了项目列表,您可以使用 .str.join:

将它们合并在一起
>>> df['Tag'] = tags.str.join(', ')
>>> df
                  Name              Ad_data              Tag
a  this row is a horse  buy a nice REDHORSE  horse, redhorse

现在如果我们想 clean 我们可以使用 validTags 中的顺序。请注意 tags 只是 df['Tag'].str.split(', ')

>>> tags = tags.explode()
>>> tags
a       horse
a    redhorse
Name: 0, dtype: object
>>> tag_order = validTags.str.lower().reset_index(name='tag').rename(columns={'index': 'order'})
>>> tag_order
   order       tag
0      0     horse
1      1  redhorse
>>> ordered_tags = pd.merge(tags.reset_index(name='tag'), tag_order, on='tag')
>>> ordered_tags
  index       tag  order
0     a     horse      0
1     a  redhorse      1
>>> clean = ordered_tags.loc[ordered_tags.groupby('index')['order'].idxmax()].set_index('index')['tag']
>>> clean
index
a    redhorse
Name: tag, dtype: object

如您所见,order 列现在定义了哪些标签是最具体的,而 GroupBy + idxmax 允许通过排序获得最具体的标签。

>>> df['clean tag'] = clean
>>> df
                  Name              Ad_data              Tag clean tag
a  this row is a horse  buy a nice REDHORSE  horse, redhorse  redhorse