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

长答案:(或者为什么你得到的结果是正确的,考虑到你使用的参数)这可能从文档中不清楚,但在这个设置中打开和关闭是关于严格与非- 严格的不等式(例如&lt; vs &lt;=)。

一个例子应该可以清楚地说明这一点。使用示例中的内部间隔,这与更改 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的关系的主要内容,如果未能解决你的问题,请参考以下文章

工作随笔——Swift中的Range和一些字符操作

参数类型 'Range<Int>' 不符合预期的类型 'Sequence' Swift3

在 Swift 5 中,如何以可读的方式(使用整数)显示从 range.UpperBound 或 range.LowerBound 返回的原始位?

SAP CRM WebClient UI的on_new_focus应该怎么理解

如何检测用户何时在 Swift 中完成移动 Range 滑块

swift学习笔记 - RangeClosedRangeCountableClosedRange与CountableRange学习