2.11 Swift 3 理解Range和Collection的关系
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2.11 Swift 3 理解Range和Collection的关系相关的知识,希望对你有一定的参考价值。
参考技术A 在之前Swift操作符的内容里,我们曾经提到了两个和范围有关的的操作符:实际上,这两个区间操作符在Swift中,是通过两个struct来实现的,叫做CountableRange和CountableClosedRange,它们都遵从Comparable和Strideable protocol。
其中:
只有半开半闭区间可以表达“空区间”的概念,例如:5 ..< 5,而5 ... 5则包含一个元素5;
只有闭区间可以包含区间位置的最大值,例如:1 ... Int.max,而1 ..< Int.max则表示1 ... (Int.max - 1);
这是Swift 3对range操作符的一个改进,在Swift 2中,闭区间是通过半开半闭区间模拟出来的。因此,实际上你没法在Swift 2中表达[1, Int.max]这个范围。为了解决这个问题,在Swift 2中还特别加入了两个类型:HalfOpenInterval和ClosedInterval这两个类型来表达正确的范围,但是在Swift 3里,它们已经被删掉了。
之所以这两个range操作符背后的类型都用Countable开头,意思是指它们是可以被迭代的,也就是可以从头到尾计算范围的值。例如:
这样,Swift就会从1打印到5。
既然有CountableRange,就不难联想到,是否有uncountable的版本呢?实际上,的确是存在的。只是,它们仅能表示一个区间,但我们不能遍历它。来看下面的例子:
这时,Swift编译器就会提示我们ClosedRange<Double>没有遵从Sequence protocol。于是,uncountable的版本就出现了,就是这个ClosedRange。当然还有一个uncountable的半开半闭区间的类型,叫做Range。
为了遍历这样的浮点数区间,我们只能用stride(from:to:by:)和stride(from:though:by:)来指定起始、结束范围以及步进值。前者,类似于半开半闭区间,后者类似于闭区间:
于是,按照一个区间可以表示的范围,以及它是否可以被遍历,实际上Swift中一共有4种不同的区间类型:
相信在后续的Swift版本里,还会对这一系列区间类型进行改进和优化。但至少现在,它还是会给我们带来一些麻烦。对于一个接受Range<T>类型参数的函数来说,我们甚至无法传递一个ClosedRange<T>类型的对象。
为什么会这样呢?其实,和(Closed)Range无法通过for循环遍历一样,我们无从根据一个CloseRange<T>的结束位置,找到闭区间结束位置的上一个位置,因此,这种转换是无法完成的。如果从Swift语言的角度来说,就是,(Closed)Range仅仅实现了Comparable protocol,而没有实现Strideable protocol。
因此,面对这类情况,我们只能自己根据ClosedRange<T>计算需要的范围,再重新创建正确的Range<T>对象。
如何理解熊猫重采样方法中的封闭和标签参数?
【中文标题】如何理解熊猫重采样方法中的封闭和标签参数?【英文标题】:how to understand closed and label arguments in pandas resample method? 【发布时间】:2018-06-28 15:46:09 【问题描述】:基于此处的 pandas 文档:Docs
还有例子:
>>> index = pd.date_range('1/1/2000', periods=9, freq='T')
>>> series = pd.Series(range(9), index=index)
>>> series
2000-01-01 00:00:00 0
2000-01-01 00:01:00 1
2000-01-01 00:02:00 2
2000-01-01 00:03:00 3
2000-01-01 00:04:00 4
2000-01-01 00:05:00 5
2000-01-01 00:06:00 6
2000-01-01 00:07:00 7
2000-01-01 00:08:00 8
Freq: T, dtype: int64
重采样后:
>>> series.resample('3T', label='right', closed='right').sum()
2000-01-01 00:00:00 0
2000-01-01 00:03:00 6
2000-01-01 00:06:00 15
2000-01-01 00:09:00 15
在我看来,重新采样后的 bin 应该是这样的:
=========bin 01=========
2000-01-01 00:00:00 0
2000-01-01 00:01:00 1
2000-01-01 00:02:00 2
=========bin 02=========
2000-01-01 00:03:00 3
2000-01-01 00:04:00 4
2000-01-01 00:05:00 5
=========bin 03=========
2000-01-01 00:06:00 6
2000-01-01 00:07:00 7
2000-01-01 00:08:00 8
我在这一步上正确吗??
所以.sum
之后我觉得应该是这样的:
2000-01-01 00:02:00 3
2000-01-01 00:05:00 12
2000-01-01 00:08:00 21
我只是不明白它是怎么出来的:
2000-01-01 00:00:00 0
(因为在这种情况下,label='right'
, 2000-01-01 00:00:00 不能是任何 bin 的任何右边缘)。
2000-01-01 00:09:00 15
(原始系列中甚至不存在标签 2000-01-01 00:09:00。
【问题讨论】:
不,closed='right'
表示它是另一种方式。
@JohnE 感谢您的回复。我理解 closed=right 意味着右边缘包含在间隔中。你能告诉我series.resample('3T', label='right', closed='right')
重新采样后的3个箱子是什么样子吗?我认为2000-01-01 00:00:00 0
不应该出现在.sum
之后。
我以为我已经在回答中解决了这个问题,但我会尽量让它更清楚
我认为这是因为您正在使用“3T”重新采样,这表示每个样本的时间为 3 分钟,而不是 3 行
【参考方案1】:
根据 JohnE 的回答,我整理了一个有用的信息图,可以一劳永逸地解决这个问题:
【讨论】:
【参考方案2】:重采样是通过首先生成一个栅格来执行的,该栅格是一系列瞬间(不是周期、间隔、持续时间),并且它独立于“标签”和“闭合”参数来完成,这一点很重要。它仅使用“freq”参数和“loffset”。在您的情况下,系统将生成以下栅格:
2000-01-01 00:00:00
2000-01-01 00:03:00
2000-01-01 00:06:00
2000-01-01 00:09:00
请再次注意,目前没有区间或周期方面的解释。你可以使用'loffset'来移动它。
然后系统将使用'close'参数在两个选项中进行选择:
(开始,结束]
[开始,结束)
这里的 start 和 end 是栅格中两个相邻的时间戳。 'label' 参数用于选择是否使用 start 或 end 作为间隔的代表。
在您的示例中,如果您选择 closed='right' 那么您将获得以下间隔:
( previous_interval , 2000-01-01 00:00:00] - 0
(2000-01-01 00:00:00, 2000-01-01 00:03:00] - 1,2,3
(2000-01-01 00:03:00, 2000-01-01 00:06:00] - 1,2,3
(2000-01-01 00:06:00, 2000-01-01 00:09:00] - 4,5,6
(2000-01-01 00:09:00, next_interval ] - 7,8
请注意,在您聚合这些间隔内的值后,结果会根据“标签”参数显示为两个版本,也就是说,相同的间隔是否由其左侧或右侧时间戳表示。
【讨论】:
【参考方案3】:简答:如果你使用closed='left'
和loffset='2T'
,那么你会得到你所期望的:
series.resample('3T', label='left', closed='left', loffset='2T').sum()
2000-01-01 00:02:00 3
2000-01-01 00:05:00 12
2000-01-01 00:08:00 21
长答案:(或者为什么你得到的结果是正确的,考虑到你使用的参数)这可能从文档中不清楚,但在这个设置中打开和关闭是关于严格与非- 严格的不等式(例如<
vs <=
)。
一个例子应该可以清楚地说明这一点。使用示例中的内部间隔,这与更改 closed
的值不同:
closed='right' => ( 3:00, 6:00 ] or 3:00 < x <= 6:00
closed='left' => [ 3:00, 6:00 ) or 3:00 <= x < 6:00
您可以在许多地方找到区间符号(括号与括号)的解释,例如这里: https://en.wikipedia.org/wiki/Interval_(mathematics)
label
参数仅控制显示左侧 (3:00) 还是右侧 (6:00),而不影响结果本身。
另请注意,您可以使用loffset
参数(应作为时间增量输入)更改间隔的起点。
回到示例,我们只将标签从“右”更改为“左”:
series.resample('3T', label='right', closed='right').sum()
2000-01-01 00:00:00 0
2000-01-01 00:03:00 6
2000-01-01 00:06:00 15
2000-01-01 00:09:00 15
series.resample('3T', label='left', closed='right').sum()
1999-12-31 23:57:00 0
2000-01-01 00:00:00 6
2000-01-01 00:03:00 15
2000-01-01 00:06:00 15
如您所见,结果是一样的,只是索引标签发生了变化。 Pandas 只允许您显示右侧或左侧标签,但如果它显示 both,那么它看起来像这样(下面我使用标准索引符号,其中左侧的 (
表示打开并且右侧]
表示关闭):
( 1999-12-31 23:57:00, 2000-01-01 00:00:00 ] 0 # = 0
( 2000-01-01 00:00:00, 2000-01-01 00:03:00 ] 6 # = 1+2+3
( 2000-01-01 00:03:00, 2000-01-01 00:06:00 ] 15 # = 4+5+6
( 2000-01-01 00:06:00, 2000-01-01 00:09:00 ] 15 # = 7+8
请注意,第一个 bin (23:57:00,00:00:00] 不为空,只是它包含一行并且该行中的值为零。如果将“sum”更改为'count' 这变得更加明显:
series.resample('3T', label='left', closed='right').count()
1999-12-31 23:57:00 1
2000-01-01 00:00:00 3
2000-01-01 00:03:00 3
2000-01-01 00:06:00 2
【讨论】:
您知道如何将重新采样日期作为新列分配给原始数据框吗?例如,300*24 行,日期列来自%Y-%m-%d %H
(每小时 300 天)。我需要每 7 天 groupby 他们,最后一天锚到今天,以 7* 24 回滚。因为某些原因我不能使用 resample.agg ,所以我需要将重新采样的日期列设置回原始数据框
@Mithril 这是一个有趣的问题,您可能想发布一个新问题以获得一个好的和具体的答案。在我的脑海中,我可以看到 2 种替代方法:(1) 使用 merge_asof
合并回原始数据,或者 (2) 使用 interpolate
而不是重新采样以上是关于2.11 Swift 3 理解Range和Collection的关系的主要内容,如果未能解决你的问题,请参考以下文章
参数类型 'Range<Int>' 不符合预期的类型 'Sequence' Swift3
在 Swift 5 中,如何以可读的方式(使用整数)显示从 range.UpperBound 或 range.LowerBound 返回的原始位?
SAP CRM WebClient UI的on_new_focus应该怎么理解
如何检测用户何时在 Swift 中完成移动 Range 滑块
swift学习笔记 - RangeClosedRangeCountableClosedRange与CountableRange学习