优化此 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_data
和 Name
列中提取这些标签:
>>> 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
首先,我对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_data
和 Name
列中提取这些标签:
>>> 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