pandas 数据帧中 parse/split URL 的 pythonic 方式
pythonic way to parse/split URLs in a pandas dataframe
我有一个 df,它有成千上万个链接,如下所示,针对不同的用户,在标记为 url:
的列中
https://www.google.com/something
https://mail.google.com/anohtersomething
https://calendar.google.com/somethingelse
https://www.amazon.com/yetanotherthing
我有以下代码:
import urlparse
df['domain'] = ''
df['protocol'] = ''
df['domain'] = ''
df['path'] = ''
df['query'] = ''
df['fragment'] = ''
unique_urls = df.url.unique()
l = len(unique_urls)
i=0
for url in unique_urls:
i+=1
print "\r%d / %d" %(i, l),
split = urlparse.urlsplit(url)
row_index = df.url == url
df.loc[row_index, 'protocol'] = split.scheme
df.loc[row_index, 'domain'] = split.netloc
df.loc[row_index, 'path'] = split.path
df.loc[row_index, 'query'] = split.query
df.loc[row_index, 'fragment'] = split.fragment
该代码能够正确解析和拆分 url,但速度很慢,因为我要遍历 df 的每一行。有没有更有效的方法来解析 URL?
我认为当您写回 df
时发生了太多查找。看起来每个 df.loc[row_index, ...]
需要检查的行数与总共 url 行一样多(df.url
的大小)。这意味着首先您至少查看所有行一次以找到唯一的 urls,然后对于每个 url 您再次执行此操作以找到匹配的行,然后再次为每次写入。因此,假设 unique
只进行一次完整扫描,那么您平均扫描 table 次 1+N+(5N/2)
次。你真的应该只需要一次。
除非您有大量重复,否则您可以忽略重复项,逐行遍历 df
并确保每次迭代都使用整数索引。 (.iloc
) 如果您不在行中存储其他数据,您也可以一次分配所有字段:
df.iloc[idx] = {'protocol': ..., 'domain': ..., ...}
您可以使用 Series.map
在一行中完成相同的操作:
df['protocol'],df['domain'],df['path'],df['query'],df['fragment'] = zip(*df['url'].map(urlparse.urlsplit))
使用 timeit,当 运行 在 186 个 url 上时,每个循环 2.31 ms
中的 运行 而不是原始方法中每个循环的 179 ms
。 (但是请注意,该代码未针对重复项进行优化,并且将 运行 通过 urlparse 多次相同的 url。)
完整代码:
import pandas
urls = ['https://www.google.com/something','https://mail.google.com/anohtersomething','https://www.amazon.com/yetanotherthing'] # tested with list of 186 urls instead
df['protocol'],df['domain'],df['path'],df['query'],df['fragment'] = zip(*df['url'].map(urlparse.urlsplit))
我有一个 df,它有成千上万个链接,如下所示,针对不同的用户,在标记为 url:
的列中https://www.google.com/something
https://mail.google.com/anohtersomething
https://calendar.google.com/somethingelse
https://www.amazon.com/yetanotherthing
我有以下代码:
import urlparse
df['domain'] = ''
df['protocol'] = ''
df['domain'] = ''
df['path'] = ''
df['query'] = ''
df['fragment'] = ''
unique_urls = df.url.unique()
l = len(unique_urls)
i=0
for url in unique_urls:
i+=1
print "\r%d / %d" %(i, l),
split = urlparse.urlsplit(url)
row_index = df.url == url
df.loc[row_index, 'protocol'] = split.scheme
df.loc[row_index, 'domain'] = split.netloc
df.loc[row_index, 'path'] = split.path
df.loc[row_index, 'query'] = split.query
df.loc[row_index, 'fragment'] = split.fragment
该代码能够正确解析和拆分 url,但速度很慢,因为我要遍历 df 的每一行。有没有更有效的方法来解析 URL?
我认为当您写回 df
时发生了太多查找。看起来每个 df.loc[row_index, ...]
需要检查的行数与总共 url 行一样多(df.url
的大小)。这意味着首先您至少查看所有行一次以找到唯一的 urls,然后对于每个 url 您再次执行此操作以找到匹配的行,然后再次为每次写入。因此,假设 unique
只进行一次完整扫描,那么您平均扫描 table 次 1+N+(5N/2)
次。你真的应该只需要一次。
除非您有大量重复,否则您可以忽略重复项,逐行遍历 df
并确保每次迭代都使用整数索引。 (.iloc
) 如果您不在行中存储其他数据,您也可以一次分配所有字段:
df.iloc[idx] = {'protocol': ..., 'domain': ..., ...}
您可以使用 Series.map
在一行中完成相同的操作:
df['protocol'],df['domain'],df['path'],df['query'],df['fragment'] = zip(*df['url'].map(urlparse.urlsplit))
使用 timeit,当 运行 在 186 个 url 上时,每个循环 2.31 ms
中的 运行 而不是原始方法中每个循环的 179 ms
。 (但是请注意,该代码未针对重复项进行优化,并且将 运行 通过 urlparse 多次相同的 url。)
完整代码:
import pandas
urls = ['https://www.google.com/something','https://mail.google.com/anohtersomething','https://www.amazon.com/yetanotherthing'] # tested with list of 186 urls instead
df['protocol'],df['domain'],df['path'],df['query'],df['fragment'] = zip(*df['url'].map(urlparse.urlsplit))