我正面临有关我的代码中过程的数据结构挑战,在该过程中,我需要在正例和负例中计算字符串的频率。 这是一个很大的瓶颈,我似乎无法找到更好的解决方案。
我必须遍历数据集中的每个长字符串,并提取子字符串,我需要计算其子频率。在一个完美的世界中,以下形状的熊猫数据框将是完美的:
最后,预期的结构类似于
string | frequency positive | frequency negative
________________________________________________
str1 | 5 | 7
str2 | 2 | 4
...
但是,对于明显的性能限制,这是不可接受的。
我的解决方案是使用字典来跟踪行,并使用Nx2
numpy矩阵来跟踪频率。之所以这样做,是因为在此之后,无论如何我都需要将频率放在Nx2
numpy矩阵中。
当前,我的解决方案是这样的:
str_freq = np.zeros((N, 2), dtype=np.uint32)
str_dict = {}
str_dict_counter = 0
for i, string in enumerate(dataset):
substrings = extract(string) # substrings is a List[str]
for substring in substrings:
row = str_dict.get(substring, None)
if row is None:
str_dict[substring] = str_dict_counter
row = str_dict_counter
str_dict_counter += 1
str_freq[row, target[i]] += 1 # target[i] is equal to 1 or 0
但是,这确实是我的代码的瓶颈,我想加快速度。
关于此代码的某些内容是不可压缩的,例如extract(string)
,因此必须保留该循环。但是,如果可能的话,使用并行处理没有问题。
我特别想知道的是,是否有一种方法可以改善内循环。众所周知,Python的循环不好,但是这个循环似乎毫无意义,但是由于(据我所知)我们无法像使用numpy数组那样对字典进行多次获取和设置,所以我不知道该怎么做改善它。
您建议做什么?是用某些较低级别的语言重写的唯一解决方案吗?
虽然我也打算使用SQL-lite,但是我不知道这是否值得。 记录下来,这大约需要10MB的数据,目前大约需要45秒,但是每次都需要用新数据重复进行。
编辑:添加示例以测试自己
import random
import string
import re
import numpy as np
import pandas as pd
def get_random_alphaNumeric_string(stringLength=8):
return bytes(bytearray(np.random.randint(0,256,stringLength,dtype=np.uint8)))
def generate_dataset(n=10000):
d = []
for i in range(n):
rnd_text = get_random_alphaNumeric_string(stringLength=1000)
d.append(rnd_text)
return d
def test_dict(dataset):
pattern = re.compile(b"(q.{3})")
target = np.random.randint(0,2,len(dataset))
str_freq = np.zeros((len(dataset)*len(dataset[0]), 2), dtype=np.uint32)
str_dict = {}
str_dict_counter = 0
for i, string in enumerate(dataset):
substrings = pattern.findall(string) # substrings is a List[str]
for substring in substrings:
row = str_dict.get(substring, None)
if row is None:
str_dict[substring] = str_dict_counter
row = str_dict_counter
str_dict_counter += 1
str_freq[row, target[i]] += 1 # target[i] is equal to 1 or 0
return str_dict, str_freq[:str_dict_counter,:]
def test_df(dataset):
pattern = re.compile(b"(q.{3})")
target = np.random.randint(0,2,len(dataset))
df = pd.DataFrame(columns=["str","pos","neg"])
df.astype(dtype={"str":bytes, "pos":int, "neg":int}, copy=False)
df = df.set_index("str")
for i, string in enumerate(dataset):
substrings = pattern.findall(string) # substrings is a List[str]
for substring in substrings:
check = substring in df.index
if not check:
row = [0,0]
row[target[i]] = 1
df.loc[substring] = row
else:
df.loc[substring][target[i]] += 1
return df
dataset = generate_dataset(1000000)
d,f = test_dict(dataset) # takes ~10 seconds on my laptop
# to get the value of some key, say b'q123'
f[d[b'q123'],:]
d = test_df(dataset) # takes several minutes (hasn't finished yet)
# the same but with a dataframe
d.loc[b'q123']