我有一个数据点列表。对于我的程序的完整运行,我将使用所有数据点,但是为了测试代码,我想只使用它们中的一小部分,以便程序在短时间内运行。不过,我不想简单地采用列表的前n个元素;我想从列表中选择元素的均匀分布。因此,如果我使用50%的数据点,我可能希望每隔一个数据点从数据点列表中进行选择。
基本上,我想要一个以列表和百分比作为参数的函数,并返回一个由输入列表中的偶数元素分布组成的列表,其数量尽可能接近请求的百分比。
这样做的好方法是什么?
答案 0 :(得分:4)
为完整起见,请考虑以下事项。
问题可以分为两部分:
在给定一定百分比或分数的情况下,确定要挑选的元素数量。
选择列表中应选择的元素。
第一点是直截了当。如果您希望获得列表的percentage = 35. #%
,那么您最好选择round(len(my_list) * (percentage / 100.))
个元素。请注意,仅当len(my_list)
是(percentage / 100.)
的倍数时,您才能获得完全正确的百分比。这种不准确性是不可避免的,因为连续测量(百分比)被转换为离散测量(nbr。元素)。
第二点将取决于您应该返回哪个元素的特殊要求。选择尽可能均匀分布的元素是可行的,但肯定不是最简单的方法。
以下是您如何在概念上执行此操作(请参阅下面的实现):
如果您有一个长度l
的列表,那么您需要将某个平均分布的分数f
(f = percentage / 100.
)包含在内,您必须将列表中的索引加入到大小为round(l * f)
的{{1}}个箱子。你想要的是每个箱子中最核心元素的列表。
为什么这样做?
对于第一点,请注意,如果我们制作大小为l / round(l * f)
的分档,我们最后会得到l / round(l * f)
个分箱。哪个是理想的数量(见上文第1点)。如果对于每个这些大小相同的箱子,我们选择最中心的元素,我们得到尽可能均匀分布的元素列表。
这是一个简单的(既不是速度优化也不是非常漂亮)的实现:
l / l / round(l * f) = round(l * f)
我们现在可以将这个理想算法(equal_dist_els)与@jonrsharpe的切片方法进行比较:
请参阅下面的代码。
沿着x轴是要返回的元素的希望部分,并且在y轴上是所需分数和两种方法返回的分数之间的绝对差。我们看到,对于大约0.7(~70%)的分数,切片方法的偏差是显着的,即如果要求~70%切片方法返回所有元素(100%),这是几乎45%的偏差。 / p>
总之,我们可以说@jonrsharpe的切片方法适用于小分数(from bisect import bisect_left
def equal_dist_els(my_list, fraction):
"""
Chose a fraction of equally distributed elements.
:param my_list: The list to draw from
:param fraction: The ideal fraction of elements
:return: Elements of the list with the best match
"""
length = len(my_list)
list_indexes = range(length)
nbr_bins = int(round(length * fraction))
step = length / float(nbr_bins) # the size of a single bin
bins = [step * i for i in xrange(nbr_bins)] # list of bin ends
# distribute indexes into the bins
splits = [bisect_left(list_indexes, wall) for wall in bins]
splits.append(length) # add the end for the last bin
# get a list of (start, stop) indexes for each bin
bin_limits = [(splits[i], splits[i + 1]) for i in xrange(len(splits) - 1)]
out = []
for bin_lim in bin_limits:
f, t = bin_lim
in_bin = my_list[f:t] # choose the elements in my_list belonging in this bin
out.append(in_bin[int(0.5 * len(in_bin))]) # choose the most central element
return out
)但在选择较大分数时变得越来越不准确。另请注意,不准确性与列表长度无关。实际上,分箱算法实现起来稍微复杂一些,而且很可能也慢得多。然而,它的不准确性只是上面提到的不可避免的不准确性,随着列表长度的增加而减少。
图表的代码:
>>0.1
答案 1 :(得分:2)
这可以通过设置带有步骤的切片来实现:
def select_elements(seq, perc):
"""Select a defined percentage of the elements of seq."""
return seq[::int(100.0/perc)]
使用中:
>>> select_elements(range(10), 50)
[0, 2, 4, 6, 8]
>>> select_elements(range(10), 33)
[0, 3, 6, 9]
>>> select_elements(range(10), 25)
[0, 4, 8]
您还可以添加round
,因为int
会截断:
>>> int(3.6)
3
>>> int(round(3.6))
4
如果您想使用比例而不是百分比(例如0.5
而不是50
),只需将100.0
替换为1
。