【问题标题】:how to cut a serial numbers into intervals in python code?如何在python代码中将序列号切割成间隔?
【发布时间】:2014-04-05 20:31:23
【问题描述】:

Cut 将 x 的范围划分为多个区间,并根据它们落在哪个区间对 x 中的值进行编码。我可以在 R 中获得具有 cut 函数的小组,我该怎么做 在 python 代码中工作?

raw_data='
82   68   86   94   89   63   77   76   84    89
75   78   81   82   76   99   80   84   89    88
60   83   72   83   85   56   86   68   75   100
90   84   75   86   74   77   95   63   80    76
100   43  76   81   79   74   96   52   69    86'
data<-scan(textConnection(raw_data)) 
table(cut(data,include.lowest=TRUE,right=FALSE,breaks=c(0,60,70,80,90,100))

输出是:

[0,60)  [60,70)  [70,80)  [80,90) [90,100] 
   3        6       14       20        7 

【问题讨论】:

  • 如果你使用 Pandas,那里也有类似的剪切功能。否则,为什么不自己实现呢?
  • 当我用 python 库——matplotlib 绘制图形时,做同样的事情比 R 多行(大约 3 次),但是 python 比 R 更合乎逻辑,也许它是一个好尝试用python做统计。
  • 看看你的 cmets 的答案 - 你在这里使用 Python 3.x 吗?
  • 感谢您接受我的回答。这是我提醒我对自己的回答并不完全满意的机会,我刚刚发布了我的代码的改进版本。
  • @sus_mlm : pandas 中的函数名是什么?

标签: python r


【解决方案1】:

正如 sus_mlm 建议的那样,您可以为此使用 Pandas。但是如果你想避免外部导入并且raw_data 不是太大,你可以使用:

import itertools
from collections import Counter

# 'kwargs' lets you implement more options
def cut ( data, breaks, **kwargs ):
    counts = Counter()
    ranges = itertools.izip(breaks, itertools.islice(breaks,1,None))
    for element in data:
        for range in ranges:
            if range[0] < element < range[1]:
                counts[range] += 1
                break
    return counts

# Call 'cut'
cut( raw_data, (0,60,70,80,90,100) )

Acknowledgements

【讨论】:

  • 您可能需要仔细检查一下...我很确定您应该使用breaks 而不是data 来代替ranges = ... 但是,由于您使用的是@ 987654327@ 这意味着 ranges 将在处理完数据中的第一个元素后耗尽,这意味着对于第二个项目以后,for range in ranges 是空操作。
  • 这是固定的一件事,但输出是:Counter({(77, 95): 1, (68, 94): 1})
  • AttributeError: 'module' 对象没有属性 'izip'
【解决方案2】:

我提出这个代码:

raw_data='''
82   68   86   94   89   63   77   76   84    89
75   78   81   82   76   99   80   84   89    88
60   83   72   83   85   56   86   68   75   100
90   84   75   86   74   77   95   63   80    76
100   43  76   81   79   74   96   52   69    86'''
tu = (0,60,70,80,90,100)

print raw_data
print tu

from collections import OrderedDict
tu0 = tu[0]
tuL = tu[-1]
tuF = tu[1:]
d = OrderedDict((tu[i], [tu[i-1:i+1]])
                for i in xrange(1,len(tu)))

for x in map(int,raw_data.split()):
    if x>=tu0:
        if x==tuL:
            d[tuL].append(x)
        else:
            for lim in tuF:
                if x<lim:
                    d[lim].append(x)
                    break
print
print '\n'.join('%-15s%s' % (d[k][0],len(d[k])-1) for k in d)

结果

(0, 60)        3
(60, 70)       6
(70, 80)       14
(80, 90)       20
(90, 100)      7

.

编辑

我对算法中的所有这些ifforelsebreak 都不满意。
所以我给出了一个新代码,我认为这是一个改进,因为它更短更清晰。我觉得也比较快,但是我没有测试过速度。

困难在于,当 raw_data 中的一个数字等于限制序列 tu = (0,60,70,80,90,100) 的最后一个值(样本中的 100)时,这个数字必须算作属于最后一个 span,这里是 [90,100](示例中的数学符号),而前面的跨度如 [0,60[[60,70[ ... 不包括上限。
但我终于找到了正确的测试 x==tuLast==lim 来处理这种特殊情况,然后能够使用生成器而不是之前可怕的算法,通过几个测试 if 和一个循环。

以下代码可在 Python 3 中执行。

raw_data='''
82   68   86   94   89   63   77   76   84    89
75   78   81   82   76   99   80   84   89    88
60   83   72   83   85   56   86   68   75   100
90   84   75   86   74   77   95   63   80    76
100   43  76   81   79   74   96   52   69    86'''
tu = [0,60,70,80,90,100]
print( 'raw_data :%s\n\ntu :\n%s' % (raw_data,tu),'\n' )
    
tu0, tuLast = tu[0], tu[-1]
tu_from1 = tu[1:]
from collections import OrderedDict
d = OrderedDict( (two[1],[two]) for two in zip(tu, tu_from1) )

print ('OrderedDictionary , before:\n%s'
       % '\n'.join('key:%3s   value:%s'  % item for item in d.items()))

# the core of the code is here:
for x in (x for x in map(int,raw_data.split()) if x>=tu0):
    d[next(lim for lim in tu_from1
           if x<lim or x==tuLast==lim)].append(x)

print ('\nOrderedDictionary , after:\n%s'
       % '\n'.join('key:%3s   value:%s'  % item for item in d.items()),'\n')

print( '\n'.join('%-15s%s' % (v[0],len(v)-1) for v in d.values()) )

结果

raw_data :
82   68   86   94   89   63   77   76   84    89
75   78   81   82   76   99   80   84   89    88
60   83   72   83   85   56   86   68   75   100
90   84   75   86   74   77   95   63   80    76
100   43  76   81   79   74   96   52   69    86

tu :
[0, 60, 70, 80, 90, 100] 

OrderedDictionary , before:
key: 60   value:[(0, 60)]
key: 70   value:[(60, 70)]
key: 80   value:[(70, 80)]
key: 90   value:[(80, 90)]
key:100   value:[(90, 100)]

OrderedDictionary , after:
key: 60   value:[(0, 60), 56, 43, 52]
key: 70   value:[(60, 70), 68, 63, 60, 68, 63, 69]
key: 80   value:[(70, 80), 77, 76, 75, 78, 76, 72, 75, 75, 74, 77, 76, 76, 79, 74]
key: 90   value:[(80, 90), 82, 86, 89, 84, 89, 81, 82, 80, 84, 89, 88, 83, 83, 85, 86, 84, 86, 80, 81, 86]
key:100   value:[(90, 100), 94, 99, 100, 90, 95, 100, 96] 

(0, 60)        3
(60, 70)       6
(70, 80)       14
(80, 90)       20
(90, 100)      7

【讨论】:

  • 当我运行 d = OrderedDict((tu[i], [tu[i-1:i+1]]) for i in xrange(1,len(tu))) 时,NameError: name 'xrange' is not defined.
  • 我认为你使用的 Python 3 没有更多的xrange,只有range。但如果是这样,您应该在前面的指令中使用需要括号的print,因为print() 现在是 Python 3 中的一个函数
  • 我刚刚看到您担心izipimap 涉及其他答案。这让我相信您使用 Python 3,其中 map 现在是生成器,zip 也是
【解决方案3】:

您可以使用bisect 获取它所属的“范围”,并使用defaultdict 作为每个范围内出现次数的计数器,例如:

from bisect import bisect
from collections import defaultdict
from itertools import chain, imap

raw_data="""
82   68   86   94   89   63   77   76   84    89
75   78   81   82   76   99   80   84   89    88
60   83   72   83   85   56   86   68   75   100
90   84   75   86   74   77   95   63   80    76
100   43  76   81   79   74   96   52   69    86""".lstrip()

# Make data into iterable of ints
data = chain.from_iterable(
    imap(int, line.split()) 
    for line in raw_data.splitlines()
)
# Counter
count = defaultdict(int)
for num in data:
    # Use 101 instead of 100 to cater for inclusion of 100
    count[bisect([0, 60, 70, 80, 90, 101], num)] += 1

count 的结果是:

defaultdict(<type 'int'>, {1: 3, 2: 6, 3: 14, 4: 20, 5: 7})

【讨论】:

  • 当我跑步时,NameError: global name 'imap' is not defined.
  • @it_is_a_literature 如果您使用的是 Python 3.x - 从顶部的导入中删除 imap 并将以 imap(int...) 开头的行改为 map。我不确定您为什么会收到该错误,就好像您使用的是 Python 3.x 一样,您应该得到一个 ImportError 而不是...(但是,您的其他 cmets 似乎与您是一致的可能使用 3.x)...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-01-14
  • 2020-11-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-10
  • 1970-01-01
相关资源
最近更新 更多