对于文本分类项目(年龄)我正在制作我的数据的子集。我用文件名制作了3个列表,按年龄排序。我想要对这些列表进行随机播放,然后将每个混洗列表中的5000个文件名附加到新列表中。结果应该是具有15000个文件(5000 10s,5000 20s,5000 30s)的数据子集。在你的下面你可以看到我到目前为止所写的内容。但我知道random.shuffle
返回none,而none类型的对象不可迭代。我怎么解决这个问题?
def seed():
return 0.47231099848
teens = [list of files]
tweens = [list of files]
thirthies = [list of files]
data = []
for categorie in random.shuffle([teens, tweens, thirthies],seed):
data.append(teens[:5000])
data.append(tweens[:5000])
data.append(thirthies[:5000])
答案 0 :(得分:9)
第一个问题是你正在改组由3个项目组成的列表[青少年,补间,战俘](甚至每个项目都是一个列表)而不是改组每个子列表
其次,您可以使用random.sample
代替random.shuffle
for categ in [teens, tweens, thirthies]:
data.append(random.sample(categ,5000])
或@JonClements在评论中建议您可以使用列表理解
categories = [teens, tweens, thirthies]
data = [e for categ in categories for e in random.sample(categ, 5000)]
答案 1 :(得分:7)
你是正确的random.shuffle
返回无。这是因为它将其列表参数原位混洗,并且它是一个Python约定,其函数采用可变arg并且变异它返回None
。但是,您误解了random
arg到random.shuffle
:它需要是一个随机数生成器,而不是像seed
那样始终返回相同数字的函数。
顺便说一句,您可以使用其seed
函数为随机模块提供的标准随机数生成器播种。 random.seed
接受任何可哈希的对象作为其参数,尽管习惯上将它传递给数字或字符串。你也可以传递它None
(这里相当于没有传递arg),它会随机使用系统随机源(如果没有系统随机源,那么系统时间)用作种子)。如果您在导入随机模块后未明确调用seed
,则相当于调用seed()
提供种子的好处是每次运行具有相同种子的程序时,各种随机模块函数产生的随机数将完全相同。在开发和调试代码时,这非常非常有用:当输出不断变化时,很难找到错误。 :)
有两种主要方法可以做你想要的。您可以随机播放列表,然后从中分割前5000个文件名。或者您可以使用random.sample
函数获取5000个随机样本。这样你就不需要改变整个列表了。
import random
random.seed(0.47231099848)
# teens, tweens, thirties are lists of file names
file_lists = [teens, tweens, thirties]
# Shuffle
data = []
for flist in file_lists:
random.shuffle(flist)
data.append(flist[:5000])
使用sample
# Sample
data = []
for flist in file_lists:
data.append(random.sample(flist, 5000))
我没有对此代码执行速度测试,但我怀疑sample
会更快,因为它只需要随机选择项目而不是移动所有列表项目。 shuffle
效率很高,因此您可能不会注意到运行时间的差异,除非您的青少年,补间和三十年代文件列表的文件名都多于5000个。
这两个循环都使data
成为包含3个子列表的嵌套列表,每个子列表中包含5000个文件名。但是,如果您希望它是15000个文件名的平面列表,则只需使用list.extend
方法而不是list.append
。例如,
data = []
for flist in file_lists:
data.extend(random.sample(flist, 5000))
或者我们可以使用带有双for
循环的列表推导来完成它:
data = [fname for flist in file_lists for fname in random.sample(flist, 5000)]
如果您需要过滤data
的内容以构建最终文件列表,最简单的方法是在列表推导中添加if
条件。
假设我们有一个函数可以测试文件名是否是我们要保留的文件名:
def keep_file(fname):
# if we want to keep fname, return True, otherwise return False
然后我们可以做
data = [fname for flist in file_lists for fname in random.sample(flist, 5000) if keep_file(fname)]
和data
只会包含通过keep_file
测试的文件名。
另一种方法是使用生成器表达式而不是列表推导来创建文件名,然后将其传递给内置的filter
函数:
data_gen = filter(keep_file, (fname for flist in file_lists for fname in random.sample(flist, 5000)))
data_gen
本身就是一个迭代器。你可以像这样建立一个列表:
data_final = list(data_gen)
或者,如果您实际上并不需要将所有名称作为集合,并且您可以逐个处理它们,则可以将其放在for
循环中,如下所示:
for fname in data_gen:
print(fname)
# Do other stuff with fname
这使用较少的RAM,但缺点是它“消耗”文件名,因此一旦for
循环完成data_gen
将为空。
假设您编写了一个从每个文件中提取所需数据的函数:
def age_and_text(fname):
# Do stuff that extracts the age and desired text from the file
return fname, age, text
您可以像这样创建(filename, age, text)
个元组的列表:
data_gen = (fname for flist in file_lists for fname in random.sample(flist, 5000) if keep_file(fname))
final_data = [age_and_text(fname) for fname in data_gen]
请注意我的第一个代码段中的切片:flist[:5000]
。这需要flist
中的前5000个项目,包含索引0到4999的项目。您的版本有teens[:5001]
,这是一个错误的错误。切片的工作方式与范围相同。因此range(5000)
产生从0到4999的5000个数字。它以这种方式工作,因为Python(像大多数现代编程语言一样)使用从零开始的索引。
答案 2 :(得分:5)
shuffle
返回None
,这是不可迭代的
你应该做
data = []
for category in [teens, tweens, thirthies]:
category_copy = category[:]
random.shuffle(category_copy, seed)
data.append(category_copy[:5000])
答案 3 :(得分:2)
random.shuffle
更改列表本身(使其改组)。所以看起来你想要这样的东西:
teens = [list of files]
tweens = [list of files]
thirthies = [list of files]
random.shuffle(teens)
random.shuffle(tweens)
random.shuffle(thirthies)
data = []
for categorie in [teens, tweens, thirthies] :
data.append(categorie[:5000])
BTW somelist[:n]
将被截断为n
元素,请检查:
>>> [1,2,3,4,5][:3]
[1, 2, 3]