关于音频场景识别的一个基于python的程序合集dcase_util,是英文的,所以在此将其翻译成了中文,以便查阅。
dcase_util文档描述了为声场景和事件检测和分类(DCASE)创建的实用程序集合。这些实用程序最初是为DCASE挑战基线系统(2016和2017)创建的,并捆绑到独立库中以允许其在其他研究项目中重复使用。
这些实用程序的主要目标是简化研究代码,使其更易读,更易于维护。大多数实施的实用程序都与音频数据集相关:处理元数据和各种形式的其他结构化数据,并为各种来源的音频数据集提供标准化的使用API。
dcase_util中包含的概念有:
- Dataset:一组音频信号和与之相关的参考标注。
- Container:类存储数据并基于要存储的数据类型,提供有意义且清晰的数据访问。
- Metadata:数据注释。
- Repository:容器来存储多个数据容器。
- Encoder:用于将数据从不同类型转换为另一类型。
- Processor:为处理数据提供统一API的类。
- ProcessingChain:在一个链中连接多个数据处理器,允许构建数据的复杂处理。
在搭建好了python环境之后,使用下列pip命令安装dcase_util。
1 |
pip install dcase_util |
下面是各个单元的使用教程
1.Container
该库提供数据container以简化工作流程。 这些container是从标准的Python container(例如对象,列表和字典)继承的,以使它们可以与其他工具和库一起使用。 这些数据的目的是为了包装数据,使用有用的方法来访问和操作数据,以及加载和存储数据。
1.1基本用法
- 四种从文件中加载内容的方式:
1 |
# 1 |
- 将内容保存到文件
1 |
dict_container.save(filename='test.yaml') |
- 查看并在控制器中打印container内容
1 |
dict_container.show() |
- 查看并在标准日志记录系统中打印
1 |
dict_container.log() |
如果日志记录系统在调用之前未被初始化,那么将使用带默认参数的dcase_util.utils.setup_logging来初始化它。
1.2 字典
dcase_util.containers.DictContainer
设计用于嵌套字典比标准字典数据container更容易一些。它允许通过所谓的虚线路径或路径部分列表访问嵌套字典中的字段。
- 用字典初始化container
1 |
dict_container = dcase_util.containers.DictContainer( |
- 初始化container,从文件中加载内容
1 |
dict_container = dcase_util.containers.DictContainer().load(filename='test.yaml') |
- 通过点路径(dotted path)访问字段:
1 |
# Field exists |
- 通过路径部分列表访问字段
1 |
# Field exists |
- 通过点路劲设置字段
1 |
dict_container.set_path('test.field2', 200) |
- 获取嵌套字典中所有叶节点的点路径
1 |
dict_container.get_leaf_path_list() |
- 获取嵌套字典中以’field’开头的所有叶节点的虚线路径
1 |
dict_container.get_leaf_path_list(target_field_startswith='field') |
- 把container存成.yaml文件
1 |
dict_container.save(filename='test.yaml') |
- 从.yaml文件中加载container内容
1 |
dict_container.load(filename='test.yaml') |
1.3 字典列表
dcase_util.containers.ListDictContainer
是用于存储dcase_util.containers.DictContainer
的列表。
- 用字典列表初始化container
1 |
listdict_container = dcase_util.containers.ListDictContainer( |
- 根据键和值访问列表中的项目
1 |
print(listdict_container.search(key='field1', value=10)) |
- 得到字典中特定字段的值
1 |
print(ld.get_field(field_name='field2')) |
1.4 Data Containers
- 其中三种是可用的几种数据容器类型:
dcase_util.containers.DataArrayContainer
,数组数据的数据容器,内部数据存储在numpy.array中。(可用)dcase_util.containers.DataMatrix2DContainer
,二维数据矩阵的数据容器,内部数据存储在二维numpy.ndarray中。(可用)- case_util.containers.DataMatrix3DContainer`,用于三维数据矩阵的数据容器,内部数据存储在3-D numpy.ndarray中。(可用)
dcase_util.containers.BinaryMatrixContainer
,用于二维二进制数据矩阵的数据容器,内部数据存储在二维numpy.ndarray中。(不可用)
- 用随机矩阵10x100初始化container,并将时间分辨率设置为20ms:
1 |
data_container = dcase_util.containers.DataMatrix2DContainer( |
当存储例如声学特征时,时间分辨率对应于特征提取帧跳长。
- 直接访问数据矩阵:
1 |
print(data_container.data.shape) |
- 显示container信息
1 |
data_container.show() |
该容器具有聚焦机制,可灵活捕捉数据矩阵的一部分。 可以根据时间进行对焦(如果时间分辨率已定义,则以秒为单位),或基于帧ID。
- 使用焦点在0.5秒和1.0秒之间获取部分数据:
1 |
print(data_container.set_focus(start_seconds=0.5, stop_seconds=1.0).get_focused().shape) |
- 使用焦点获取第10帧和第50帧之间的零件数据:
1 |
print(data_container.set_focus(start=10, stop=50).get_focused().shape) |
- 重置焦点并访问完整的数据矩阵:
1 |
data_container.reset_focus() |
- 访问帧1,2,10和30
1 |
data_container.get_frames(frame_ids=[1,2,10,30]) |
- 访问帧1-5,每列只有第一个值:
1 |
data_container.get_frames(frame_ids=[1,2,3,4,5], vector_ids=[0]) |
- 转置矩阵:
1 |
transposed_data = data_container.T |
- 绘制数据:
1 |
data_container.plot() |
dcase_util.containers.BinaryMatrixContainer
提供与DataMatrix2DContainer
相同的用途,但是用于二进制内容。
1.5 存储库(Repositories)
dcase_util.containers.DataRepository
和dcase_util.containers.FeatureRepository
是可用于存储多个其他数据容器的容器。 存储库存储具有两个级别信息的数据:标签和流。 标签是更高级别的密钥,流是第二级。
例如,可以使用储存库来储存与相同音频信号有关的多个不同声学特征。 流ID可用于存储从不同音频通道提取的特征。 后面的功能可以使用提取器标签和流ID进行访问。
- 用数据初始化容器:
1 |
data_repository = dcase_util.containers.DataRepository( |
- 显示container信息:
1 |
data_repository. show() |
- 访问存储库中的数据:
1 |
data_repository.get_container(label='label1',stream_id='stream1') |
- 设置数据
1 |
data_repository.set_container(label='label3',stream_id='stream0', container={'data':500}) |
2. 音频
dcase_util.containers.AudioContainer
是多声道音频的数据容器。它读取多种格式(WAV,FLAC,M4A,WEBM)并写入WAV和FLAC文件。 直接从Youtube下载音频内容也受支持。
2.1 创建container
- 创建双通道的音频container
1 |
audio_container = dcase_util.containers.AudioContainer(fs=44100) |
- 显示的container信息
1 |
# AudioContainer :: Class |
2.2 加载和保存
加载
1
2
3audio_container = dcase_util.containers.AudioContainer().load(
filename=dcase_util.utils.Example.audio_filename()
)显示的container信息
1 |
# AudioContainer :: Class |
- 从Youtube加载content
1 |
audio_container = dcase_util.containers.AudioContainer().load_from_youtube( |
2.3 焦点段(Focus segment)
container具有聚焦机制,可灵活捕捉部分音频数据,同时保持完整的音频信号不变。 可以根据时间进行聚焦(如果时间分辨率已定义,则以秒为单位),或基于样本ID。 可以对单声道或混音(单声道)频道进行聚焦。 音频容器内容可以通过冻结来替代焦点细分。
- 使用焦点在0.5秒和1.0秒之间获取部分数据:
1 |
print(audio_container.set_focus(start_seconds=0.5, stop_seconds=1.0).get_focused().shape) |
- 使用焦点从5秒开始持续2秒获取部分数据:
1 |
print(audio_container.set_focus(start_seconds=5, duration_seconds=2.0).get_focused().shape) |
- 使用焦点从5秒开始持续2秒获取部分数据,混合两个立体声声道:
1 |
print(audio_container.set_focus(start_seconds=5, duration_seconds=2.0, channel='mixdown').get_focused().shape) |
- 使用焦点从5秒开始2秒开始数取部分数据,在两个立体声通道左侧:
1 |
print(audio_container.set_focus(start_seconds=5, duration_seconds=2.0, channel='left').get_focused().shape) |
- 使用焦点从5秒开始2秒开始数取部分数据,秒音频通道(索引从0开始):
1 |
print(audio_container.set_focus(start_seconds=5, duration_seconds=2.0, channel=1).get_focused().shape) |
- 使用焦点获取样本44100和88200之间的零件数据:
1 |
print(audio_container.set_focus(start=44100, stop=88200).get_focused().shape) |
- 重置焦点并访问完整的数据矩阵:
1 |
audio_container.reset_focus() |
- 使用焦点从5秒开始2秒开始数取部分数据,,并冻结该部分
1 |
audio_container.set_focus(start_seconds=5, duration_seconds=2.0).freeze() |
2.4 处理
- 正则化音频
1 |
audio_container.normalize() |
- 对音频重采样到目标采样率:
1 |
audio_container.resample(target_fs=16000) |
2.5 可视化
- 绘图波形:
1 |
audio_container.plot_wave() |
- 绘制频谱
1 |
audio_container.plot_spec() |
3. 声学特征
库提供基本的声学特征提取器:dcase_util.features.MelExtractor
,dcase_util.features.MfccStaticExtractor
,dcase_util.features.MfccDeltaExtractor
,dcase_util.features.MfccAccelerationExtractor
,dcase_util.features.ZeroCrossingRateExtractor
,dcase_util.features.RMSEnergyExtractor
和dcase_util.features.SpectralCentroidExtractor
。
3.1 特征提取
- 为音频信号提取梅尔带能量(使用默认参数):
1 |
# Get audio in a container, mixdown of a stereo signal |
- 为特定音频段提取梅尔带能量:
1 |
# Get audio in a container, mixdown of a stereo signal |
- 直接从numpy矩阵中提取特征:
1 |
# Create an audio signal |
库中提供的所有音频提取器适用于单声道音频。
3.2 可视化
- 绘制提取的特征
1 |
# Get audio in a container, mixdown of a stereo signal |
4. 数据处理
4.1 数据操作
有几个不同的实用程序来操作数据:
dcase_util.data.Normalizer
,计算归一化因子和归一化数据。dcase_util.data.RepositoryNormalizer
,一次性标准化数据存储库。dcase_util.data.Aggregator
,聚合滑动处理窗口中的数据。dcase_util.data.Sequence
r,对数据矩阵进行排序。dcase_util.data.Stacker
,基于给定的矢量配方堆叠数据矩阵。dcase_util.data.Selector
,根据具有开始和偏移量的事件选择数据的数据段。dcase_util.data.Masker
,基于具有开始和偏移的事件来掩盖数据的数据段。
4.1.1 归一化
dcase_util.data.Normalizer
类可用于计算数据的归一化因子(平均值和标准偏差),而不一次读取所有数据。在小部分读取数据时累计中间统计数据。
- 逐个文件计算归一化因子:
1 |
data = dcase_util.utils.Example.feature_container() |
- 使用声明:
1 |
data = dcase_util.utils.Example.feature_container() |
- 用预先计算的值初始化标准化器(normalizer):
1 |
data = dcase_util.utils.Example.feature_container() |
- 归一化数据
1 |
data = dcase_util.utils.Example.feature_container() |
4.1.2 聚合(Aggregator)
数据聚合器类(dcase_util.data.Aggregator)可用于在滑动处理窗口中处理数据矩阵。 这个处理阶段可以用来通过计算它们的平均值和标准偏差来折叠具有特定窗口长度的数据,或者将该矩阵平坦化为单个向量。
支持的处理方法:flatten、mean、std、cov、kurtosis、skew。所有这些处理方法都可以结合使用。
- 计算分为10帧窗口的均值和标准差,以1帧跳跃(Calculating mean and standard deviation in 10 frame window, with 1 frame hop):
1 |
data = dcase_util.utils.Example.feature_container() |
- 将具有10帧的数据矩阵压缩成一个单独的向量,具有1帧跳跃(with 1 frame hop):
1 |
data = dcase_util.utils.Example.feature_container() |
4.1.3 测序(Sequencing)
Sequencer类(dcase_util.data.Sequencer)将数据矩阵处理成序列(图像)。序列可以重叠,并且可以在调用之间改变排序网格(移位)。
- 创建序列:
1 |
data = dcase_util.utils.Example.feature_container() |
4.1.4 堆叠(Stacker)
Stacker类(dcase_util.data.Stacker
)根据配方堆叠存储在数据存储库中的数据。例如,可以使用此类创建包含使用多个特征提取器提取的数据的新特征矩阵。使用配方,您可以选择全矩阵,只有部分具有开始和结束索引的数据向量,或选择单个数据行。
例:
1 |
# Load data repository |
4.2 数据编码
数据编码器可用于将参考元数据转换为二进制矩阵。
- one hot
OneHotEncoder类(dcase_util.data.OneHotEncoder
)可用于创建二进制矩阵,其中单个类在整个信号中处于活动状态。该编码器适用于多类单标签分类应用。
例:
1 |
# Initilize encoder |
- Many-hot
ManyHotEncoder类(dcase_util.data.ManyHotEncoder
)可用于创建二进制矩阵,其中多个类在整个信号中处于活动状态。 该编码器适用于多类多标签分类应用,如音频标签。
例:
1 |
# Initilize encoder |
- 事件滚动(Event roll)
EventRollEncoder类(dcase_util.data.EventRollEncoder)可用于创建二进制矩阵,其中多个事件在指定的时间段内处于活动状态。 该编码器适用于事件检测应用。
例子:
1 |
# Metadata |
5. Metadata
Library提供容器dcase_util.containers.MetaDataContainer,用于处理来自大多数DCASE相关应用程序区域的元数据:声场景分类,事件检测和音频标记。
原则上,元数据是一个包含元项目字典的列表,它可以像普通列表一样使用。
5.1 创建容器(container)
用声场列表初始化元数据容器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16meta_container_scenes = dcase_util.containers.MetaDataContainer(
[
{
'filename': 'file1.wav',
'scene_label': 'office'
},
{
'filename': 'file2.wav',
'scene_label': 'street'
},
{
'filename': 'file3.wav',
'scene_label': 'home'
}
]
)用声音事件列表初始化元数据容器:
1 |
meta_container_events = dcase_util.containers.MetaDataContainer( |
- 用标签初始化元数据容器:
1 |
meta_container_tags = dcase_util.containers.MetaDataContainer( |
- 显示内容
1 |
meta_container_scenes.show() |
- 显示内容和每个元数据项目:
1 |
meta_container_scenes.show_all() |
5.2 加载和保存
- 将元数据保存到文件中:
1 |
meta_container_events.save(filename='events.txt') |
- 从注释文件加载元数据:
1 |
meta_container_events = dcase_util.containers.MetaDataContainer().load( |
5.3 访问数据
- 获取元数据中提及的音频文件及其数量:
1 |
print(meta_container_events.unique_files) |
- 获取独特的场景标签及其数量:
1 |
print(meta_container_scenes.unique_scene_labels) |
- 获取元数据中使用的特定事件标签及其数量:
1 |
print(meta_container_events.unique_event_labels) |
- 获取元数据中使用的独特标签及其数量:
1 |
print(meta_container_tags.unique_tags) |
5.4 过滤(Filtering)
- 基于文件名过滤元数据:
1 |
filtered = meta_container_events.filter(filename='file1.wav') |
- 基于事件标签过滤元数据:
1 |
filtered = meta_container_events.filter(event_label='speech') |
- 基于文件和事件标签过滤元数据:
1 |
filtered = meta_container_events.filter(filename='file1.wav', event_label='speech') |
- 基于时间段过滤,并使段开始新的零时间:
1 |
filtered = meta_container_events.filter_time_segment(filename='file1.wav', start=12, stop=24) |
5.5 处理(Processing)
- 将时间偏移量添加到元数据项中设置的开始和偏移量:
1 |
meta_container_events.add_time(time=10) |
- 删除非常短的事件并合并它们之间有小间隙的事件(相同的事件标签):
1 |
meta_container_events = dcase_util.containers.MetaDataContainer( |
5.6 事件滚动(Event roll)
将事件列表转换为事件滚动(具有事件活动的二进制矩阵):
1 |
meta_container_events = dcase_util.containers.MetaDataContainer( |
6. Datasets
数据库类提供在库中为许多不同组织的音频数据集创建统一的接口。数据集将在第一次使用时下载,提取并准备好使用情况。
提供了四种类型的数据集:
- 声场景数据集,从
dcase_util.datasets.AcousticSceneDataset
类继承的类。 - 声音事件数据集,从
dcase_util.datasets.SoundEventDataset
类继承的类。 - 声音事件数据集合成数据创建,从
dcase_util.datasets.SyntheticSoundEventDataset
类继承的类。 - 音频标记数据集,从
dcase_util.datasets.AudioTaggingDataset
类继承的类。
获取所有可用数据集的列表:
1 |
print(dcase_util.datasets.dataset_list()) |
6.1 初始化数据集
要下载,提取和准备数据集(在这种情况下,数据集将放置在临时目录中,并且只下载与元数据相关的文件):
1 |
import tempfile |
6.2 交叉验证步骤
通常数据集通过交叉验证设置提供。
获得fold训练材料1:
1 |
training_material = db.train(fold=1) |
- 获取fold1的测试材料(无参考数据的材料):
1 |
testing_material = db.test(fold=1) |
- 使用完整的参考数据获取fold1的测试材料:
1 |
eval_material = db.eval(fold=1) |
- 要将所有数据集合折叠为无:(To get all data set fold to None)
1 |
all_material = db.train(fold=None) |
- 迭代所有fold:
1 |
for fold in db.folds: |
大多数数据集不提供验证集拆分。 但是,数据集类提供了几种方法从训练集中生成它,同时保持数据统计数据并确保在训练集和验证集中不会有来自同一源的数据。
为fold1生成平衡的验证集(平衡完成,以便将来自相同位置的记录分配给同一集):
1 |
training_files, validation_files = db.validation_split( |
- 为fold1生成随机验证集(不平衡):
1 |
training_files, validation_files = db.validation_split( |
- 获取数据集提供的验证集(示例中使用的数据集不提供它,这会引发错误。):
1 |
training_files, validation_files = db.validation_split( |
6.3 元数据(Meta data)
- 获取与该文件关联的元数据:
1 |
items = db.file_meta(filename='audio/b086_150_180.wav') |
7. 处理链(Processing chain)
除了基本的实用程序外,该库还提供了将各种数据处理类链接在一起的机制。这样可以更轻松地构建复杂的数据处理管道。数据处理在包含在链中的处理器(处理器列表)中完成。
所有处理器类都使用dcase_util.processors.ProcessorMixin mixin
和特定的实用程序类。例如,dcase_util.processors.AudioReadingProcessor
从dcase_util.processors.ProcessorMixin
和dcase_util.containers.AudioContainer
继承。
- 音频相关处理器:
dcase_util.processors.AudioReadingProcessor
,音频读取,支持多声道音频。dcase_util.processors.MonoAudioReadingProcessor
,在多声道音频通道的情况下,音频读取将混合到单个通道中。
- 数据处理处理器:
dcase_util.processors.AggregationProcessor
,通过滑动处理窗口聚合数据。dcase_util.processors.SequencingProcessor
,将数据矩阵分割成序列。dcase_util.processors.NormalizationProcessor
,规范数据矩阵。dcase_util.processors.RepositoryNormalizationProcessor
,规范存储在仓库中的数据。dcase_util.processors.StackingProcesso
r根据存储在存储库中的数据堆叠新的特征矩阵。
- 数据编码处理器:
dcase_util.processors.OneHotEncodingProcessor
,one-hot编码(分类)。dcase_util.processors.ManyHotEncodingProcessor
,many-hot编码(标记)。dcase_util.processors.EventRollEncodingProcessor
,事件滚动编码(检测)。
特征提取处理器:
dcase_util.processors.RepositoryFeatureExtractorProcessor
,同时提取许多要素类型,并将它们存储到单个存储库中。支持多声道音频输入。dcase_util.processors.MelExtractorProcessor
,提取梅尔带能量。仅支持单声道音频。dcase_util.processors.MfccStaticExtractorProcessor
,提取静态MFCC。仅支持单声道音频。dcase_util.processors.MfccDeltaExtractorProcessor
,提取delta MFCC。仅支持单声道音频。dcase_util.processors.MfccAccelerationExtractorProcessor
,提取加速MFCC。仅支持单声道音频。dcase_util.processors.ZeroCrossingRateExtractorProcessor
,提取过零率。仅支持单声道音频。dcase_util.processors.RMSEnergyExtractorProcessor
,提取RMS能量。仅支持单声道音频。dcase_util.processors.SpectralCentroidExtractorProcessor
,提取光谱质心。仅支持单声道音频。
元数据处理器:
dcase_util.processors.MetadataReadingProcesso
r,从文件中读取元数据。
7.1 特征提取和处理
- 为音频文件提取梅尔带能量:
1 |
# Define processing chain |
- 关注音频的某些部分:
1 |
# Define processing chain |
- 为音频文件提取几个不同的声学特征,并形成数据矩阵:
1 |
# Define processing chain |
- 为音频文件提取几个不同的声学特征,对它们进行归一化处理,形成数据矩阵,沿时间轴聚合(上下文窗口化),以及将数据拆分成序列:
1 |
import numpy |
7.2 元数据处理(Meta data processing)
- 获得事件卷(event roll):
1 |
import tempfile |
- 获取用于焦点段事件卷:
1 |
import tempfile |
应用的例子
声场景分类器
本教程演示如何使用dcase_util中的实用程序构建简单的声场景分类器。 声场景分类器应用程序通常包含以下阶段:
- 数据集初始化阶段,使用数据集已下载并准备使用。
- 特征提取阶段将为开发数据集中的所有音频文件提取声学特征,并存储到磁盘以便稍后访问
- 特征标准化阶段,通过交叉验证折叠训练材料并计算声学特征的均值和标准差以稍后对特征数据进行归一化
- 学习阶段,通过交叉验证折叠的培训材料,并学习声学模型。
- 测试阶段,通过交叉验证折叠测试材料,并估计每个样本的场景类别。
- 评估,评估系统输出与地面真相。
本例使用DCASE2013(10场景类),静态MFCC作为特征,GMM作为分类器发布的声场景数据集。 例子只显示了最少的代码,通常的开发系统需要更好的参数化来使系统开发更容易。
完整的代码示例可以找到examples / asc_gmm_simple.py
。
- 数据集初始化
本示例使用针对DCASE2013发布的声场景数据集,处理此类的数据集类随dcase_utils:dcase_util.datasets.DCASE2013_Scenes_DevelopmentSet
一起提供。
数据集需要先下载,解压缩到磁盘,并准备好使用:
1 |
import os |
- 特征提取
通常,为所有音频文件提取特征并将其存储在磁盘上是最有效的,而不是在每次需要声学特征时都提取它们。 示例如何执行此操作:
1 |
log.section_header('Feature Extraction') |
- 特征标准化(Feature normalization)
在这个阶段,每个交叉验证折叠都会通过培训材料,并计算声学特征的均值和标准差。 这些归一化因子用于在特征数据在学习和测试阶段使用之前对其进行归一化。
1 |
log.section_header('Feature Normalization') |
- 模型学习
在这个阶段,虽然通过交叉验证折叠了训练材料,并且学习和存储声学模型。
1 |
log.section_header('Learning') |
- 测试
在这个阶段,测试材料通过每次交叉验证折叠,并且针对每个测试样本估计场景类别。
1 |
log.section_header('Testing') |
- 评估
在这个阶段,系统输出是根据数据集提供的地面实况来评估的。
1 |
log.section_header('Evaluation') |
- Results:
Scene | Fold 1 | Fold 2 | Fold 3 | Fold4 | Fold 5 | header 2 | header 2 |
---|---|---|---|---|---|---|---|
row 1 col 1 | row 1 col 2 | row 1 col 2 | row 1 col 2 | row 1 col 2 | row 1 col 2 | row 1 col 2 | |
row 2 col 1 | row 2 col 2 |
Scene | Fold 1 | Fold 2 | Fold 3 | Fold 4 | Fold 5 | Average |
---|---|---|---|---|---|---|
bus | 100.00 | 100.00 | 100.00 | 100.00 | 100.00 | 100.00 |
busystreet | 100.00 | 33.33 | 33.33 | 100.00 | 66.67 | 66.67 |
office | 66.67 | 100.00 | 100.00 | 66.67 | 100.00 | 86.67 |
openairmarket | 66.67 | 100.00 | 0.00 | 66.67 | 100.00 | 66.67 |
park | 33.33 | 33.33 | 0.00 | 33.33 | 33.33 | 26.67 |
quietstreet | 66.67 | 100.00 | 33.33 | 66.67 | 66.67 | 66.67 |
restaurant | 66.67 | 0.00 | 66.67 | 0.00 | 33.33 | 33.33 |
supermarket | 33.33 | 0.00 | 33.33 | 0.00 | 33.33 | 20.00 |
tube | 100.00 | 33.33 | 33.33 | 66.67 | 66.67 | 60.00 |
tubestation | 0.00 | 66.67 | 66.67 | 0.00 | 0.00 | 26.67 |
Average | 63.33 | 56.67 | 46.67 | 50.00 | 60.00 | 55.33 |