如何提取Python列表中一定比例的均匀分布的元素?
How should a certain percentage of evenly-distributed elements of a Python list be extracted?
我有一个数据点列表。对于我程序的完整 运行,我将使用所有数据点,但为了测试代码,我只想使用其中的一小部分,以便程序 运行很短的时间。不过,我不想简单地获取列表的前 n 个元素;我想 select 均匀分布列表中的元素。因此,如果我使用了 50% 的数据点,我可能希望每隔一个数据点从数据点列表中 select。
基本上,我想要一个函数,它将一个列表和一个百分比作为参数,returns一个由输入列表中均匀分布的元素组成的列表,其数量与可能达到要求的百分比。
执行此操作的好方法是什么?
这可以通过设置带有步骤的切片来简单地实现:
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
。
为了完整起见,请考虑以下内容。
问题可以分为两部分:
确定要选择的元素数量,给定一定的百分比或分数。
选择应从列表中选择的元素。
第一点直截了当。如果您想要列表中的 percentage = 35. #%
个元素,最好选择 round(len(my_list) * (percentage / 100.))
个元素。请注意,只有当 len(my_list)
是 (percentage / 100.)
的倍数时,您才能获得完全正确的百分比。这种不准确是不可避免的,因为连续的测量值(百分比)被转换为离散的测量值(元素数量)。
第二点将取决于您对应返回哪个元素的特殊要求。选择尽可能均匀分布的元素是可行的,但肯定不是最简单的方法。
从概念上讲,您将如何执行此操作(请参阅下面的实现):
如果你有一个长度为 l
的列表,你想要其中的某个均匀分布的分数 f
(f = percentage / 100.
) 你将不得不将你列表的索引放入round(l * f)
个大小为 l / round(l * f)
的垃圾箱。你想要的是每个 bin 最中心元素的列表。
为什么这样做?
对于第一点,请注意,如果我们制作大小为 l / round(l * f)
的分箱,我们将在最后得到 l / l / round(l * f) = round(l * f)
个分箱。这是理想的数量(见上文第 1 点)。如果对于这些大小相等的容器中的每一个,然后我们选择最中心的元素,我们将得到一个尽可能均匀分布的元素列表。
这是一个简单的(既没有优化速度也没有非常漂亮)实现:
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
我们现在可以将这种理想算法 (equal_dist_els) 与@jonrsharpe 的切片方法进行比较:
请参阅下面的代码。
沿 x 轴是希望返回的元素分数,在 y 轴上是所需分数与两种方法返回的分数之间的绝对差值。我们看到对于大约 0.7(~70%)的分数,切片方法的偏差是显着的,即如果你要求~70%,切片方法 returns 所有元素(100%)几乎是偏差45%.
总而言之,我们可以说@jonrsharpe 的切片方法适用于较小的分数 (>>0.1
),但在选择较大的分数时会变得越来越不准确。另请注意,不准确性与列表的长度无关。分箱算法的实现当然稍微复杂一些,而且很可能也慢得多。然而,它的不准确性只是由上面提到的不可避免的不准确性给出的,随着列表长度的增加而减少。
地块代码:
from matplotlib import pyplot as plt
# def of equal_dist_els see above
def select_els(seq, perc):
"""Select a defined percentage of the elements of seq."""
return seq[::int(round(1./perc if perc != 0 else 0))]
list_length = 50
my_list = range(list_length)
percentages = range(1, 101)
fracts = map(lambda x: x * 0.01, percentages)
equal_dist = map(lambda x: abs(len(equal_dist_els(my_list, x)) / float(len(my_list)) - x), fracts)
slicing = map(lambda x: abs(len(select_els(my_list, x)) / float(len(my_list)) - x), fracts)
plt.plot(fracts, equal_dist, color='blue', alpha=0.8, linewidth=2, label=r'equal_dist_elements')
plt.plot(fracts, slicing, color='red', alpha=0.8, linewidth=2, label=r'select_elements by @jonrsharpe')
plt.title('Choosing equally dist. fraction of els from a list of length %s' % str(list_length))
plt.xlabel('requested fraction')
plt.ylabel('absolute deviation')
plt.legend(loc='upper left')
plt.show()
我有一个数据点列表。对于我程序的完整 运行,我将使用所有数据点,但为了测试代码,我只想使用其中的一小部分,以便程序 运行很短的时间。不过,我不想简单地获取列表的前 n 个元素;我想 select 均匀分布列表中的元素。因此,如果我使用了 50% 的数据点,我可能希望每隔一个数据点从数据点列表中 select。
基本上,我想要一个函数,它将一个列表和一个百分比作为参数,returns一个由输入列表中均匀分布的元素组成的列表,其数量与可能达到要求的百分比。
执行此操作的好方法是什么?
这可以通过设置带有步骤的切片来简单地实现:
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
。
为了完整起见,请考虑以下内容。
问题可以分为两部分:
确定要选择的元素数量,给定一定的百分比或分数。
选择应从列表中选择的元素。
第一点直截了当。如果您想要列表中的 percentage = 35. #%
个元素,最好选择 round(len(my_list) * (percentage / 100.))
个元素。请注意,只有当 len(my_list)
是 (percentage / 100.)
的倍数时,您才能获得完全正确的百分比。这种不准确是不可避免的,因为连续的测量值(百分比)被转换为离散的测量值(元素数量)。
第二点将取决于您对应返回哪个元素的特殊要求。选择尽可能均匀分布的元素是可行的,但肯定不是最简单的方法。
从概念上讲,您将如何执行此操作(请参阅下面的实现):
如果你有一个长度为 l
的列表,你想要其中的某个均匀分布的分数 f
(f = percentage / 100.
) 你将不得不将你列表的索引放入round(l * f)
个大小为 l / round(l * f)
的垃圾箱。你想要的是每个 bin 最中心元素的列表。
为什么这样做?
对于第一点,请注意,如果我们制作大小为 l / round(l * f)
的分箱,我们将在最后得到 l / l / round(l * f) = round(l * f)
个分箱。这是理想的数量(见上文第 1 点)。如果对于这些大小相等的容器中的每一个,然后我们选择最中心的元素,我们将得到一个尽可能均匀分布的元素列表。
这是一个简单的(既没有优化速度也没有非常漂亮)实现:
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
我们现在可以将这种理想算法 (equal_dist_els) 与@jonrsharpe 的切片方法进行比较:
请参阅下面的代码。
沿 x 轴是希望返回的元素分数,在 y 轴上是所需分数与两种方法返回的分数之间的绝对差值。我们看到对于大约 0.7(~70%)的分数,切片方法的偏差是显着的,即如果你要求~70%,切片方法 returns 所有元素(100%)几乎是偏差45%.
总而言之,我们可以说@jonrsharpe 的切片方法适用于较小的分数 (>>0.1
),但在选择较大的分数时会变得越来越不准确。另请注意,不准确性与列表的长度无关。分箱算法的实现当然稍微复杂一些,而且很可能也慢得多。然而,它的不准确性只是由上面提到的不可避免的不准确性给出的,随着列表长度的增加而减少。
地块代码:
from matplotlib import pyplot as plt
# def of equal_dist_els see above
def select_els(seq, perc):
"""Select a defined percentage of the elements of seq."""
return seq[::int(round(1./perc if perc != 0 else 0))]
list_length = 50
my_list = range(list_length)
percentages = range(1, 101)
fracts = map(lambda x: x * 0.01, percentages)
equal_dist = map(lambda x: abs(len(equal_dist_els(my_list, x)) / float(len(my_list)) - x), fracts)
slicing = map(lambda x: abs(len(select_els(my_list, x)) / float(len(my_list)) - x), fracts)
plt.plot(fracts, equal_dist, color='blue', alpha=0.8, linewidth=2, label=r'equal_dist_elements')
plt.plot(fracts, slicing, color='red', alpha=0.8, linewidth=2, label=r'select_elements by @jonrsharpe')
plt.title('Choosing equally dist. fraction of els from a list of length %s' % str(list_length))
plt.xlabel('requested fraction')
plt.ylabel('absolute deviation')
plt.legend(loc='upper left')
plt.show()