2个不稳定版本
0.2.0 | 2021年3月31日 |
---|---|
0.0.10 | 2020年2月19日 |
#12 in #合并
520KB
650 行
分形分析
各种Rust和Futhark函数,有助于执行Z-Box Merging分形分析
lib.rs
:
欢迎来到 fractal-analysis
类型库!在这里,您将找到基于“Z-box merging”算法分析分形集所需的各种工具。
如何使用
我已尽力为以下数据集创建了便于使用的函数
对于这些数据集中的每一个,您都会找到现成的函数,可以为您提供有用的结果;只需输入 example_code_here
,您就会得到结果。
注意事项
目前(2021年3月),文档和测试仍未完成。它们都是尽最大努力提供的;感谢用户的理解。
如何扩展到其他用途
哦,我多么希望有一种方法可以设计一个API,它说“哦,只需对任何输入执行 zbox_merge(input)
就可以了,它会工作的!”。
遗憾的是,它远没有这么简单。基本库可以让你走得很远,但在开始之前,你需要做大量的工作。最简单的方法是直接 给库维护者发送电子邮件 并直接询问,但在无法联系的情况下,以下是你需要做的事情
选择依赖和独立维度
首先,决定对于你的用例,“独立维度”是什么。你的脑电图中的每个通道是否是依赖于时间戳的独立维度,还是它们是二维头皮的采样?你的图像的颜色通道呢?这将给你一个初步的想法,并确定最终结果的限制。
特别需要注意的是:您希望维数的数量与测量方法无关。例如:对于颜色通道,我们选择每个通道都是一个单独的维度,部分原因是所有彩色图像都有这3个相同的通道。但对于脑电图(EEGs),我们选择将其视为一个二维网格,否则结果将取决于电极的数量,不同的设置之间的测量无法立即进行比较。因此,大约一半的精神努力都花在决定如何精确地将电极位置细分到四分位上!
计算你有多少位可用
对于每个坐标,计算它可以取的值的数量。取这些数量中最小的一个,找到它的2的底数对数,并丢弃所有的十进制数字。结果是每个坐标需要归一化的位数。
选择你的数据类型
这只有在您真的关心效率的情况下才重要。默认选择应该适用于大多数情况,但在特殊情况下可能不适用。
示例1:如果您有5个12位的键,每个Coor
将是一个u16
,因此默认的Key
将是一个u128
。但实际上它们可以放入一个u64
中;编译器只是不知道这一点。在这种情况下,您可能更喜欢显式地将所有键转换为u64
。
示例2:如果您需要48位来存储键,并且您正在16位架构的CPU上运行代码,您可能更喜欢实现定制的48位数据类型(3*16),而不是让代码默认使用u64
。lindel
库允许这样做。
创建一个“键提取”函数
键提取由两个部分组成:归一化和线性化。
归一化已经为您准备好了,您只需要为每个坐标提供最小/最大值以及它们将被归一化的位数。但是请注意,您将需要提取独立的坐标以及相关的坐标!
至于线性化,由于lindel
库的存在,已经提供了两种方法。这些是Morton编码(“Z索引”)和Hilbert编码(“Hilbert索引”)。如果您关心速度并且至少有一个独立的坐标,Z索引将更适合您。如果您没有任何独立的坐标,并且可以承担轻微的性能损失,Hilbert索引将轻松地允许您独立地检查数据集中几乎每一个可能的子集。但是请注意以下事项
- 您需要手动执行此操作,因为程序无法自动执行此操作。
- 理论上您可以在任何地方使用Hilbert索引,但在细分过程中,您可能会省略一些值太大的独立坐标。
- 虽然Z索引的计算速度比Hilbert索引快得多,但对于大数据集来说,最昂贵的操作是排序,因此对于程序的
O(N)
部分,这种差异可能并不重要。
选择一个排序算法
如果您不太关心速度,可以选择在std
或rayon
中定义的默认方法。如果您需要榨取最后一点性能,另一方面,您可能更喜欢实现基数排序或其他类似方法。
您就完成了!
…完成了初步工作,也就是说。现在剩下的事情就是编写实际代码:提取键,排序,提取每对连续键之间的公共前导位数,从它们中获取结果,移除无用位,并使用回归函数获取结果。下面是一些示例代码,复制自img_funs
模块。
let width = input.width() as usize;
let height = input.height() as usize;
let get_key_from_sample = |(x, y, rgb): (u32, u32, &image::Rgb<u8>)| -> u64 {
let norm_x = create_normaliser::<u32, u8>(0, input.width()).unwrap();
// The provided normaliser simplifies things slightly.
let norm_y = create_normaliser::<u32, u8>(0, input.height()).unwrap();
let arr = [norm_x(x), norm_y(y), rgb[0], rgb[1], rgb[2]];
// Extract both independent and dependent coordinates
morton_encoding::morton_encode(arr)
// Linearise using `morton_encoding`
};
let clzs = get_clzs(input.enumerate_pixels(), get_key_from_sample);
let (tmp, lacun) = get_results_from_clzs(clzs);
let tmp = drain_useless_bits::<_, 64, 40>(tmp);
let lacun = drain_useless_bits::<_, 64, 40>(lacun);
finalise_results(tmp, lacun, width*height, 8)
当然,如果你需要为每个键超过256位,那么你需要更改其他函数,以便它们在u16
上操作。
解释结果
分形维数
关于分形维数,最重要的理解是其对数-对数图将落在哪些限制内。这些限制形成了一个四边形,其四条边如下定义
- 边1是垂直的,对于
x
等于可用的位数。你无法将数据集细分超过每个坐标的分辨率。 - 边2是水平的,对于
y
等于可用的样本数。无论你细分多少次,你都无法获得比数据集中存在的样本(例如像素)数量更多的填充框。 - 边3是斜线,其斜率等于任何独立的坐标的数量。
- 边4也是斜线;其斜率等于可用的总坐标数量,无论是独立的还是依赖的。受限于
N
维的数据集的分形维数永远不会超过N
。
这四个限制的最直接结果是,如果对数-对数图最初急剧上升,那么它将在达到总样本量时立即遇到一个平台,之后将是水平的。因此,在计算分形维数之前,必须切除图的水平部分,否则它将人为地偏低。
第二件需要理解的事情是,尽管理想分形形状的对数-对数图将是线性的,但在实践中,数据集将是尺度依赖的,导致对数-对数图中的非线性行为。在我们发现的每个此类实例中,对数-对数图都是向下凹的。因此,用户有两个选择
- 对对数-对数图进行简单的线性回归,并将其斜率解释为分形维数。如果存在,则可以从线与实际图之间的均方误差中找到尺度依赖性。
- 对对数-对数图进行抛物线回归,将线性参数解释为分形维数,将二次参数解释为尺度依赖性。
这两种方法都未经过任何严格测试;我们诚挚地邀请用户分享任何结果。
空隙率
在现有文献中定义的空隙率似乎是一种针对每个尺度都不同的度量。这里为了用户的方便进行了测量,但结果的解释必须留给用户。
依赖关系
~11MB
~49K SLoC