处理异构语义类型

支持多种异构的 stypes,包括但不限于 multicategoricaltimestampembedding。 在本教程中,我们将向您展示一个使用 处理异构数据类型的简单示例。

处理异构列

首先,让我们创建一个包含许多不同样本类型的数据集。

import random

import numpy as np
import pandas as pd

# Numerical column
numerical = np.random.randint(0, 100, size=10)

# Categorical column
simple_categories = ['Type 1', 'Type 2', 'Type 3']
categorical = np.random.choice(simple_categories, size=100)

# Timestamp column
time = pd.date_range(start='2023-01-01', periods=100, freq='D')

# Multicategorical column
categories = ['Category A', 'Category B', 'Category C', 'Category D']
multicategorical = [
    random.sample(categories, k=random.randint(0, len(categories)))
    for _ in range(100)
]

# Embedding column (assuming an embedding size of 5 for simplicity)
embedding_size = 5
embedding = np.random.rand(100, embedding_size)

# Create the DataFrame
df = pd.DataFrame({
    'Numerical': numerical,
    'Categorical': categorical,
    'Time': time,
    'Multicategorical': multicategorical,
    'Embedding': list(embedding)
})

df.head()
>>>
    Numerical Categorical       Time                                  Multicategorical                                          Embedding
0         44      Type 2 2023-01-01              [Category D, Category A, Category B]  [0.2879910043632805, 0.38346222503494787, 0.74...
1         47      Type 2 2023-01-02  [Category C, Category A, Category B, Category D]  [0.0923738894608982, 0.3540466620838102, 0.551...
2         64      Type 2 2023-01-03                          [Category D, Category C]  [0.3209972413734975, 0.22126268518378278, 0.14...
3         67      Type 1 2023-01-04                          [Category C, Category A]  [0.2603409275874047, 0.5370225213757797, 0.447...
4         67      Type 2 2023-01-05                                      [Category A]  [0.46924917399024213, 0.8411401297855995, 0.90...

现在让我们将pandas.DataFrame加载到torch_frame.data.Dataset类中,以便我们可以从中生成TensorFrame表示。

dataset = Dataset(
    df, col_to_stype={
        'Numerical': stype.numerical,
        'Categorical': stype.categorical,
        'Time': stype.timestamp,
        'Multicategorical': stype.multicategorical,
        'Embedding': stype.embedding
    })
dataset.materialize()

dataset.tensor_frame
>>> TensorFrame(
    num_cols=4,
    num_rows=100,
    categorical (1): ['Categorical'],
    timestamp (1): ['Time'],
    multicategorical (1): ['Multicategorical'],
    embedding (1): ['Embedding'],
    has_target=True,
    device='cpu',
    )

对于每个stype,我们需要在stype_encoder_dict中指定其编码器。

from torch_frame.nn import (
    EmbeddingEncoder,
    LinearEmbeddingEncoder,
    LinearEncoder,
    MultiCategoricalEmbeddingEncoder,
    TimestampEncoder,
)

stype_encoder_dict = {
    stype.categorical: EmbeddingEncoder(),
    stype.numerical: LinearEncoder(),
    stype.embedding: LinearEmbeddingEncoder(),
    stype.multicategorical: MultiCategoricalEmbeddingEncoder(),
    stype.timestamp: TimestampEncoder()
}

现在我们可以为选择的模型指定stype_encoder_dict

注意

一些预实现的模型不支持所有的stypes。 例如,TabTransformer仅支持数值和分类的stypes

from torch_frame.nn.models.ft_transformer import FTTransformer

model = FTTransformer(
    channels=16,
    out_channels=1,
    num_layers=2,
    col_stats=dataset.col_stats,
    col_names_dict=dataset.tensor_frame.col_names_dict,
    stype_encoder_dict=stype_encoder_dict,
)

model(dataset.tensor_frame)
>>> tensor([[ 0.9405],
    [ 0.3857],
    [ 0.5265],
    [-0.3747],
    [ 0.7496],
    [ 0.0486],
    [ 0.2895],
    [ 0.1326],
    [ 0.4388],
    [-0.1665]], grad_fn=<AddmmBackward0>)

语义类型的自动推断

我们提供了一个简单的实用函数 infer_df_stype 来自动推断提供的 DataFrame 中不同列的 stype

infer_df_stype(df)
>>> {'Numerical': <stype.numerical: 'numerical'>,
    'Categorical': <stype.categorical: 'categorical'>,
    'Time': <stype.timestamp: 'timestamp'>,
    'Multicategorical': <stype.multicategorical: 'multicategorical'>,
    'Embedding': <stype.embedding: 'embedding'>}

然而,推理可能并不总是对你的数据正确/最佳。 我们建议你在继续之前自己再次检查正确性。

处理复杂的原始数据

通常,来自数据集的原始数据可能很复杂。 例如,不同的多类别列可能具有不同的分隔符,不同的时间列可能具有不同的时间格式。

目前,支持类型为liststr的原始列数据用于multicategorical。 您还可以通过torch_frame.data.Dataset中的col_to_sep参数为不同的列指定不同的分隔符。 如果指定了一个字符串,则所有多类别列将使用相同的分隔符。 如果提供了一个字典,我们将为每列使用指定的不同分隔符。

注意

您需要为所有多类别列指定分隔符,其中原始数据是str,否则每个单元格的值将被视为一个类别值。

这是一个处理具有多个多类别列的DataFrame的示例。

categories = ['Category A', 'Category B', 'Category C', 'Category D']
multicategorical1 = [
    random.sample(categories, k=random.randint(0, len(categories)))
    for _ in range(100)
]
multicategorical2 = [
    ','.join(random.sample(categories, k=random.randint(0, len(categories))))
    for _ in range(100)
]
multicategorical3 = [
    '/'.join(random.sample(categories, k=random.randint(0, len(categories))))
    for _ in range(100)
]
# Create the DataFrame
df = pd.DataFrame({
    'Multicategorical1': multicategorical1,
    'Multicategorical2': multicategorical2,
    'Multicategorical3': multicategorical3,
})

dataset = Dataset(
    df, col_to_stype={
        'Multicategorical1': stype.multicategorical,
        'Multicategorical2': stype.multicategorical,
        'Multicategorical3': stype.multicategorical,
    }, col_to_sep={'Multicategorical2': ',', 'Multicategorical3': '/'})

dataset.col_stats
>>>> {'Multicategorical1': {<StatType.MULTI_COUNT: 'MULTI_COUNT'>:
(['Category B', 'Category D', 'Category A', 'Category C'], [61, 60, 56, 49])},
'Multicategorical2': {<StatType.MULTI_COUNT: 'MULTI_COUNT'>:
(['Category D', 'Category A', 'Category B', 'Category C'], [53, 52, 51, 46])},
'Multicategorical3': {<StatType.MULTI_COUNT: 'MULTI_COUNT'>:
(['Category D', 'Category B', 'Category C', 'Category A'], [52, 52, 51, 46])}}

对于timestamp,您可以类似地在col_to_time_format中指定时间格式。 有关支持的格式的更多信息,请参阅strfttime文档。 如果未指定,将使用pandas内部的to_datetime()函数自动解析时间列。

dates = pd.date_range(start="2023-01-01", periods=5, freq='D')

df = pd.DataFrame({
        'Time1': dates,  # ISO 8601 format (default)
        'Time2': dates.strftime('%Y-%m-%d %H:%M:%S'),
})

df.head()
>>>        Time1                Time2
    0 2023-01-01  2023-01-01 00:00:00
    1 2023-01-02  2023-01-02 00:00:00
    2 2023-01-03  2023-01-03 00:00:00
    3 2023-01-04  2023-01-04 00:00:00
    4 2023-01-05  2023-01-05 00:00:00

dataset = Dataset(
    df, col_to_stype={
        'Time1': stype.timestamp,
        'Time2': stype.timestamp,
    }, col_to_time_format='%Y-%m-%d %H:%M:%S')

dataset.materialize()

dataset.col_stats
>>> {'Time1': {<StatType.YEAR_RANGE: 'YEAR_RANGE'>: [2023, 2023],
    <StatType.NEWEST_TIME: 'NEWEST_TIME'>: tensor([2023,    0,    4,    3,    0,    0,    0]),
    <StatType.OLDEST_TIME: 'OLDEST_TIME'>: tensor([2023,    0,    0,    6,    0,    0,    0]),
    <StatType.MEDIAN_TIME: 'MEDIAN_TIME'>: tensor([2023,    0,    2,    1,    0,    0,    0])},
    'Time2': {<StatType.YEAR_RANGE: 'YEAR_RANGE'>: [2023, 2023],
    <StatType.NEWEST_TIME: 'NEWEST_TIME'>: tensor([2023,    0,    4,    3,    0,    0,    0]),
    <StatType.OLDEST_TIME: 'OLDEST_TIME'>: tensor([2023,    0,    0,    6,    0,    0,    0]),
    <StatType.MEDIAN_TIME: 'MEDIAN_TIME'>: tensor([2023,    0,    2,    1,    0,    0,    0])}}