我想将任意列表表示为另外两个列表。第一个,称之为values
,包含原始列表中的唯一元素,第二个,称之为codes
,包含原始列表中每个元素的索引values
,以便原始列表可以重建为
orig_list = [values[c] for c in codes]
(注意:这类似于pandas.Categorical
表示系列的方式(
我创建了下面的函数来执行此分解:
def decompose(x):
values = sorted(list(set(x)))
codes = [0 for _ in x]
for i, value in enumerate(values):
codes = [i if elem == value else code for elem, code in zip(x, codes)]
return values, codes
这有效,但我想知道是否有更好/更有效的方法来实现这一目标(没有双循环?(,或者标准库中是否有可以为我做到这一点的东西。
更新:
下面的答案很棒,对我的功能有很大的改进。我已经按预期对所有工作进行了计时:
test_list = [random.randint(1, 10) for _ in range(10000)]
functions = [decompose, decompose_boris1, decompose_boris2,
decompose_alexander, decompose_stuart1, decompose_stuart2,
decompose_dan1]
for f in functions:
print("-- " + f.__name__)
# test
values, codes = f(test_list)
decoded_list = [values[c] for c in codes]
if decoded_list == test_list:
print("Test passed")
%timeit f(test_list)
else:
print("Test failed")
结果:
-- decompose
Test passed
12.4 ms ± 269 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
-- decompose_boris1
Test passed
1.69 ms ± 21.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
-- decompose_boris2
Test passed
1.63 ms ± 18.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
-- decompose_alexander
Test passed
681 µs ± 2.15 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
-- decompose_stuart1
Test passed
1.7 ms ± 3.42 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
-- decompose_stuart2
Test passed
682 µs ± 5.98 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
-- decompose_dan1
Test passed
896 µs ± 19.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
我接受斯图尔特的回答,认为这是最简单和最快的答案之一。
我对这个解决方案非常满意,尽管我仍在努力寻找更好的解决方案。
<小时 />代码
def decompose(original_list: List[Any]) -> Tuple[List[int], Dict[int, Any]]:
code_to_elem = dict(enumerate(set(original_list)))
elem_to_code = {v: k for k, v in code_to_elem.items()}
encoded_list = [elem_to_code[elem] for elem in original_list]
return encoded_list, code_to_elem
<小时 />测试运行
# t_list for test_list
t_list = [1, 2, 19, 3, 2, 19, 2, 3, 19, 1, 1, 3]
t_encoded, t_decoder = decompose(t_list)
t_decoded = [t_decoder[curr_code] for curr_code in t_encoded]
以下是重要变量的内容:
t_list
:[1, 2, 19, 3, 2, 19, 2, 3, 19, 1, 1, 3]
t_encoded
:[1, 2, 3, 0, 2, 3, 2, 0, 3, 1, 1, 0]
t_decoder
:{0: 3, 1: 1, 2: 2, 3: 19}
t_decoded
:[1, 2, 19, 3, 2, 19, 2, 3, 19, 1, 1, 3]
如果您有任何问题,请告诉我:)
即使它只是对鲍里斯答案的改进,这也算作一个答案。
我会使用index_of_values.append(values.setdefault(elem, len(values)))
作为循环体,因为它将三个字典查找减少到一个,并将分支保留在解释器之外。甚至可以为这两种方法创建局部变量,以免重复查找它们。但似乎两者的节省只有7%。
但是使用看起来很疯狂的values = defaultdict(lambda: len(values))
给出了23%。
from collections import defaultdict
def decompose(x):
values = defaultdict(lambda: len(values))
index_of_values = []
_append = index_of_values.append
for elem in x:
_append(values[elem])
return list(values), index_of_values
如果将循环替换为映射会更好:
def decompose(x):
values = defaultdict(lambda: len(values))
index_of_values = list(map(values.__getitem__, x))
return list(values), index_of_values
给 57%。如果我一直在查看函数的输出,我会发现这一点。也得到显然不会触发工厂。我不知道为什么没有。
如果词典未保留广告顺序:
return sorted(values, key=values.get), index_of_values
您可以使用简单的index
查找:
def decompose(x):
values = sorted(set(x))
return values, [values.index(v) for v in x]
如果需要更高的时间效率(因为x
非常大(,那么可以通过将values
表示为字典来实现(以换取一些内存开销(:
def decompose(x):
values = sorted(set(x))
d = {value: index for index, value in enumerate(values)}
return values, [d[v] for v in x]
如果不需要排序(或由于某种原因无法排序(,则sorted
替换为上述list
。
你可以这样做,IIUC,康明:
df['newgroup'] = df.reset_index().groupby('group')['index'].cummin()
In [1579]: df
Out[1579]:
group newgroup
0 5 0
1 4 1
2 5 0
3 6 3
4 7 4
5 8 5
6 5 0
7 3 7
8 2 8
9 5 0
10 6 3
11 7 4
12 8 5
13 8 5
14 5 0
创建一个字典,其中键是唯一值,它们映射到键中的索引(字典从 CPython 3.6 开始保持顺序(。您可以通过循环访问列表来执行此操作,如果某个元素不在字典中,则将其添加到字典中,并将其映射到添加时字典的长度。然后,在字典中查找元素的索引并将其追加到列表中。然后,仅返回键以及索引列表。
def decompose(x):
values = {}
index_of_values = [values.setdefault(elem, len(values)) for elem in x]
return list(values), index_of_values
这是线性的时空复杂性。像这样使用它:
>>> decompose([2, 1, 1, 1, 131, 42, 2])
([2, 1, 131, 42], [0, 1, 1, 1, 2, 3, 0])
使用列表推导的副作用通常是不受欢迎的,所以你可能想更明确地写出这个函数:
def decompose(x):
values = {}
index_of_values = []
for elem in x:
if elem not in values:
values[elem] = len(values)
index_of_values.append(values[elem])
return list(values), index_of_values
如果你需要像熊猫这样的东西。分类
arbi_arr=[1, 2, 3, 1, 2, 3]
value=list(dict.fromkey(arbi_arr))
code=list(range(0, len(arbi_arr)))