在 Python 3.4 中更改直方图的范围

Changing the range of the histogram in Python 3.4

这是一个显示下面列表的直方图的程序:

costlist = [48, 43, 51, 36, 6, 25, 51, 71,
                59, 70, 78, 36, 18, 84, 5, 9, 13,
                90, 71, 39, 80, 2, 69, 48, 21,
                66, 10, 37, 89, 20, 27, 7, 12,
                314, 83, 39, 31, 36, 56, 60,
                62, 23, 70, 51, 46, 40, 100,
                29, 30, 59, 37, 94, 99, 20, 88,
                10, 36, 42, 14, 24, 33, 60, 370,
                2, 30, 32, 85, 14, 52, 47, 16,
                25, 21, 29, 78, 83, 310, 43, 62,
                54, 83, 74, 52, 65, 82, 44, 94,
                83, 21, 36, 41, 67, 81, 32, 28,
                87, 62, 12]

结果是:

 Element Value  Histogram
    0-9     6
  10-19     9
  20-29    13
  30-39    15
  40-49    10
  50-59     9
  60-69     9
  70-79     7
  80-89    12
  90-99     4

但是,我希望它输出每个范围内的项目数:

Range   Value    Histogram
1 - 19   4       ****
20 - 29  5       *****
30 - 39  0
40 - 49  0
50 - 59  0
60 - 69  5       *****
70 - 79  10      **********
80 - 89  0
90 - 99  0
100+     3       ***

这是我的代码:

def production_cost():
    costlist = [48, 43, 51, 36, 6, 25, 51, 71,
                59, 70, 78, 36, 18, 84, 5, 9, 13,
                90, 71, 39, 80, 2, 69, 48, 21,
                66, 10, 37, 89, 20, 27, 7, 12,
                314, 83, 39, 31, 36, 56, 60,
                62, 23, 70, 51, 46, 40, 100,
                29, 30, 59, 37, 94, 99, 20, 88,
                10, 36, 42, 14, 24, 33, 60, 370,
                2, 30, 32, 85, 14, 52, 47, 16,
                25, 21, 29, 78, 83, 310, 43, 62,
                54, 83, 74, 52, 65, 82, 44, 94,
                83, 21, 36, 41, 67, 81, 32, 28,
                87, 62, 12]
    return costlist
def count_scores(scores, low, high):
    if
    return len([x for x in scores if x >= low and x <= high])
def histogram(costlist):
    d = {'%d-%d'%(x, x + 9):
         count_scores(costlist, x, x + 9) for x in range(0, 100, 10)}
    for k,v in sorted(d.items()):
        print ('%7s %5d'%(k,v))
def main():
    costlist = production_cost()
    print("%7s %5s %10s" %("Element", "Value", "Histogram"))
    histogram(costlist)

main()

我的代码运行正确,除了它没有星号并且范围丢失了一些东西。 0-910-19 应该合并,最后一个范围 100+.

应该加一个

编辑:这是限制条件。

您首先需要将 costlist 的每个项目映射到其正确的直方图容器中,使用数据 "similar" 到 prices 应该 return 的数据。我说 "similar" 因为例如 a 它计算 1 - 19,一减十九,这是无用的 -18,等等。

所以,首先,将 prices 更改为 return 一些 有用的 而不是那些无用的差异,例如:

def prices():
    return [1] + list(range(20, 110, 10))

列出 bin 的下限列表(每个上限当然由下一个 bin 的下限给出)。我在 range 上调用 list,因为您使用的是 Python 3(在 Python 2 中,您可以省略 `list)。

使用这些数据的最简单方法是构建一个 dict 将范围内的每个整数映射到它的 bin 编号(这样寻找给定整数的 bin 几乎是即时的,而不是花一些时间其他表述):

p = prices()
int2bin = {}
for i in range(1, len(p)):
    for j in range(p[i-1], p[i]):
        int2bin[j] = i - 1
lastbin = len(p) - 1

现在为每个整数找到 bin 是微不足道的,因此,计算每个给定 bin 中有多少整数同样微不足道:

import collections
c = collections.Counter(
    int2bin.get(i, lastbin)
    for i in costlist)

补充:OP 刚刚评论(虽然 Q 没有相应编辑)模块 collections 是不受欢迎的(当然,首先应该在问题中清楚明确地说明这些限制!) 显然是因为这是学校练习。

因此,如果您需要手动 re-implement collections.Counter,您当然可以这样做...:[=​​51=]

c = {}
for i in costlist:
    thebin = int2bin.get(i, lastbin)
    if thebin in c:
        c[thebin] += 1
    else:
        c[thebin] = 1

有 -- 六个语句(将 if/else 算作一个)而不是一个(加上导入),我们为这种特殊情况重新实现了 collections.Counter。我个人认为使用适当的高层次抽象是最好和最明智的——当然从概念上理解它们下面的内容也是明智的。

但是 计数 的概念对人类来说是如此自然(无论如何从一年级开始就被灌输给学生),我认为没有必要在这种情况下再重复一遍!

"If there are already things in the bin and you put another one there, then add one to the bin's count; if there were no things in the bin yet, so you're putting the first think in the bin, then start the bin's count at one" -- 让高中生再次真的值得吗?!

啊好吧,我没有受过教师培训,所以我想我不会抱怨 无聊 我是多么无用地重复 [=134] =] 在我 上学期间的概念——当 他们 在学校时,我从 children 那里听到完全相同的内容: -).

返回 actually-fun 内容...:

现在,只剩下打印任务了,经过一个稍微不同的header(你说你想要的那个):

print("%7s %5s %10s" %("Range", "Value", "Histogram"))

您可以循环遍历垃圾箱:

for i, lo in enumerate(p):
    if i + 1 < len(p):
        rng = '%d-%d' % (lo, p[i+1]-1)
    else:
        rng = '%d+' % lo
    val = c[i]
    stars = '*' * val
    print("%7s %5s %-10s" %(rng, val, stars))

把它们放在一起,你会看到结果:

  Range Value  Histogram
   1-19    15 ***************
  20-29    13 *************
  30-39    15 ***************
  40-49    10 **********
  50-59     9 ********* 
  60-69     9 ********* 
  70-79     7 *******   
  80-89    12 ************
  90-99     4 ****      
   100+     4 ****      

这似乎是您要的。

当然还有其他选择(例如,使用 dict 以外的其他方法来完成从整数到 bin 编号的映射)和可能需要解释的事情,具体取决于您的 Python 技能,所以, 欢迎进一步提问!

已添加:以下是 OP 在此问题上积累的最新额外限制: - 没有 if 语句 - 使用至少一个 def 不包括 main - 没有进口

def 已经存在于 prices 中,现在没有 import(在 non-Counter 版本中)。 "No if statements" 真的很荒谬(相当于在编码时必须只用左脚站立!-)但幸运的是 Python 提供了解决这些荒谬的技巧。

所以要构建计数器,让我们使用:

def counter(costlist):
    c = {}
    for i in costlist:
        thebin = int2bin.get(i, lastbin)
        try:
            c[thebin] += 1
        except KeyError:
            c[thebin] = 1

这是第一个 if 删除技巧:而不是更自发地检查 thebin 是否已经被放入 dict c,这里我们假设它有,并处理它没有时产生的异常。这实际上是一个 well-recognized Python 成语,我提倡它(甚至在我写 "Python in a Nutshell" 之前,我在那里猛烈抨击它:-) 作为 "It's easier to ask forgiveness than permission",借用 Commodore Hopper 的伟大格言(请参阅 https://www.youtube.com/watch?v=AZDWveIdqjY 我关于该主题的演讲)。

我们将使用 完全 相同的技巧来删除 other if,只需改写该片段:

for i, lo in enumerate(p):
    if i + 1 < len(p):
        rng = '%d-%d' % (lo, p[i+1]-1)
    else:
        rng = '%d+' % lo

与:

for i, lo in enumerate(p):
    try:
        rng = '%d-%d' % (lo, p[i+1]-1)
    except IndexError:
        rng = '%d+' % lo

在这里,它起作用是因为,如果 (i + 1) 不是 而不是 < len(p),正如最初由正常 if 检查的那样,索引 p[i+1] 将引发 IndexError,而 except 子句处理该问题!

现在,如果我正确地评估了 OP,我有两个预测:(A) 这还不够(更多的约束将 spring 来自虚无,例如 "no try/except statements"!-) 和 ( B) OP 将 still 不接受这个答案并用 all 约束打开另一个问题。我怀疑我是 spot-on re (A) 因为我无法想象一个老师愿意接受 try/except 如果他们禁止 much-simpler if;我只能希望我在 (B) 上错了,即 OP 会意识到他们不会再从我这里得到一点时间和精力,除非他们接受这个问题,从而不情愿地点击那个复选标记大纲并问另一个...:-)