【发布时间】:2017-12-27 22:01:31
【问题描述】:
Tl;dr 是粗体字。
我正在使用带有布尔“one-hot”图像注释的图像数据集(具体来说是 Celeba)。注释编码面部特征,如bald、male、young。现在我想制作一个自定义的 one-hot 列表(来测试我的 GAN 模型)。我想提供一个识字界面。即,我想要features[male]=True 或features.male=True 之类的东西,而不是指定features[12]=True 知道12 - 从零开始计数 - 对应于male 功能。
假设我的.txt 文件的标题是
Arched_Eyebrows Attractive Bags_Under_Eyes Bald Bangs Chubby Male Wearing_Necktie Young
我想将 Young、Bald 和 Chubby 编码。 预期的输出是
[ 0. 0. 0. 1. 0. 1. 0. 0. 1.]
因为 Bald 是标题的第四个条目,Chubby 是第六个,依此类推。 在不期望用户知道 Bald 是第四个条目等的情况下,最清晰的方法是什么?
我正在寻找一种 Pythonic 方式,而不是一定最快的方式。
理想功能
按重要性的粗略顺序:
- 实现我既定目标且已成为 Python 社区标准的方法将优先考虑。
- 用户/程序员不需要计入
.txt标头中的属性。这就是我要设计的重点。 - 不应期望用户拥有像
aenum这样的非标准库。 - 用户/程序员不需要为属性名称/可用属性引用
.txt标头。一个例子:如果用户想指定性别属性,但不知道是使用male还是female,应该很容易找到。 - 用户/程序员应该能够通过文档找到可用的属性(最好由 Sphinx api-doc 生成)。也就是说,第 4 点应该尽可能少读代码。以
dir()进行属性曝光就足以满足这一点。 - 程序员应该自然而然地发现索引工具。具体来说,零索引应该优于从一索引中减去。
- 在两个完全相同的解决方案之间,性能更好的解决方案会胜出。
示例:
我将比较和对比我立即想到的方式。 所有示例都使用:
import numpy as np
header = ("Arched_Eyebrows Attractive Bags_Under_Eyes "
"Bald Bangs Chubby Male Wearing_Necktie Young")
NUM_CLASSES = len(header.split()) # 9
1:听写理解
显然我们可以使用字典来完成:
binary_label = np.zeros([NUM_CLASSES])
classes = {head: idx for (idx, head) in enumerate(header.split())}
binary_label[[classes["Young"], classes["Bald"], classes["Chubby"]]] = True
print(binary_label)
值得一提的是,它的代码行数最少,并且是唯一一个不依赖于标准库而不依赖于内置函数的代码。至于底片,它并不完全是自我记录。要查看可用选项,您必须 print(classes.keys()) - 它不会与 dir() 一起公开。这接近于不满足功能 5,因为它要求用户知道 classes 是暴露功能 AFAIK 的字典。
2:枚举:
由于我现在正在学习 C++,所以首先想到的是Enum:
import enum
binary_label = np.zeros([NUM_CLASSES])
Classes = enum.IntEnum("Classes", header)
features = [Classes.Young, Classes.Bald, Classes.Chubby]
zero_idx_feats = [feat-1 for feat in features]
binary_label[zero_idx_feats] = True
print(binary_label)
这给出了点表示法,图像选项显示为dir(Classes)。但是,enum 默认使用单索引(原因是documented)。变通方法让我觉得enum 不是这样做的 Pythonic 方式,并且完全无法满足功能 6。
3:命名元组
这是标准 Python 库中的另一个:
import collections
binary_label = np.zeros([NUM_CLASSES])
clss = collections.namedtuple(
"Classes", header)._make(range(NUM_CLASSES))
binary_label[[clss.Young, clss.Bald, clss.Chubby]] = True
print(binary_label)
使用namedtuple,我们再次使用dir(clss) 获得点符号和自文档。但是,namedtuple 类比enum 重。我的意思是,namedtuple 具有我不需要的功能。这个解决方案似乎是我的示例中的佼佼者,但我不知道它是否满足功能 1,或者替代方案是否可以通过功能 7 “获胜”。
4:自定义枚举
我真的可以折断我的背部:
binary_label = np.zeros([NUM_CLASSES])
class Classes(enum.IntEnum):
Arched_Eyebrows = 0
Attractive = 1
Bags_Under_Eyes = 2
Bald = 3
Bangs = 4
Chubby = 5
Male = 6
Wearing_Necktie = 7
Young = 8
binary_label[
[Classes.Young, Classes.Bald, Classes.Chubby]] = True
print(binary_label)
这具有 Ex 的所有优点。 2. 但是,它有明显的缺点。我必须写出所有的特征(真实数据集中有40)只是为了零索引!当然,这是在 C++ (AFAIK) 中创建枚举的方法,但在 Python 中它不应该是必需的。这是功能 6 的轻微故障。
总结
有很多方法可以在 Python 中完成文字零索引。 您能否提供一个代码 sn-p,说明您将如何完成我所追求的目标并告诉我为什么您的方式是正确的?
(编辑:)或者解释一下为什么我的一个例子是适合这项工作的工具?
状态更新:
我还没有准备好接受答案,以防有人想要解决以下反馈/更新,或者出现任何新的解决方案。也许再过24小时?所有的回复都很有帮助,所以到目前为止我都赞成每个人。您可能想查看我用来测试解决方案的repo。 如果我的以下言论(不)准确或不公平,请随时告诉我:
零枚举:
奇怪的是,Sphinx 错误地记录了这一点(文档中只有一个索引),但它确实记录了它!我想这个“问题”不会失败任何理想的功能。
dotdict:
我觉得Map 有点矫枉过正,但dotdict 可以接受。感谢让这个解决方案与dir() 一起工作的两位回答者。但是,它似乎不能与 Sphinx “无缝协作”。
Numpy 记录:
正如所写,此解决方案比其他解决方案花费的时间要长得多。它比 namedtuple 慢 10 倍(在纯 dict 之后最快),比标准 IntEnum 慢 7 倍(在 numpy 记录之后最慢)。这在目前的规模上并不严重,也不是优先事项,但快速的 Google 搜索表明 np.in1d 实际上很慢。让我们坚持
_label = np.zeros([NUM_CLASSES])
_label[[header_rec[key].item() for key in ["Young", "Bald", "Chubby"]]] = True
除非我在链接的仓库中实现了错误。这将执行速度带入了与其他解决方案相比的范围内。再说一次,没有狮身人面像。
namedtuple(和 rassar 的批评)
我不相信您对enum 的批评。在我看来,您认为我处理问题的方法是错误的。可以告诉我这个问题,但我看不出使用namedtuple 与“Enum [which] 将为每个常量提供单独的值”有何根本不同。我误会你了吗?
无论如何,namedtuple 出现在 Sphinx 中(正确编号,因为它的价值)。在“理想功能”列表中,这与零枚举和在零枚举之前的配置文件相同。
接受的理由
我接受了零枚举的答案,因为这个答案给了我namedtuple 的最佳挑战者。 按照我的标准,namedtuple 勉强算是最好的解决方案。但是 salparadise 写的答案让我对这个评估充满信心。感谢所有回答的人。
【问题讨论】:
-
您给
namedtuple的唯一缺点是它“太重”。您并没有说它缺少您需要的功能,也没有说额外的功能会主动妨碍。所以对我来说,这是显而易见的赢家。我认为没有什么比采用一个众所周知的、易于理解的标准库功能来满足您的所有要求并直接使用它更符合 Python 的了。 -
在你的例子中,3 是最pythonic - 1 不明确,4 过于明确,通常枚举对于这样的事情不是pythonic,尤其是索引差异。 3 更具可读性,使用众所周知的标准库,并且没有真正的缺点。
-
@JohnY 查看编辑。您的评论将作为我打算提出问题的答案。
-
@rassar 请参阅上面我对 JohnY 的评论。
-
这似乎是一个不完整/过于主观的问题。在每种情况下,您都会提出不同的,有时甚至是相互矛盾的论点。对于字典示例,您提到对标准库的内置插件的偏好。然后你提到需要使用
dir()。对于 namedtuple 你提到太重了。如果你能从程序员(你)和用户的角度清楚地说明程序的目标是什么,即程序的使用方式,为什么用户必须使用dir()而不是classes.keys(),那么会更容易回答。也许还有所需功能的列表?
标签: python python-3.x