Фреймворк TabularData набирает обороты, обрабатывая таблицы данных для подготовки моделей машинного обучения. Но не позволяйте описанию на упаковке заставить вас оставить его в покое, под этим маленьким парнем скрывается огромная сила.

Например, фреймворк может быть использован для:

  1. Парсинг .csv файлов

  2. Парсинг .json файлы

  3. Импорт или экспорт

  4. Загружайте удивительные логи в свою консоль из ваших собственных моделей.

Его полезность выходит за рамки этого, но я хочу показать вам небольшой трюк, для которого я его использую, - в частности, касающийся пункта № 4 выше. Например, у тебя есть.json, подобный этому:

{
    "people":[
        {
            "name": "David",
            "mobile": 33333333,
            "hasPets": true,
            "pets": ["Dog", "Fish", "Bird"],
            "address": {
                "permanent": "France",
                "current": "UK"
            }
        },
        {
            "name": "Jordan",
            "mobile": 33333333,
            "hasPets": false,
            "pets": [],
            "address": {
                "permanent": "Austrailia",
                "current": "US"
            }
        }
    ]
}

Но представьте, что он больше, например….намного больше. Даже огромным? Скажем, 1000 или около того записей. Если бы вы должны были расшифровать такой ответ, используя пешеходные (pedestrian) модели, подобные этой:

import Foundation

struct Tennants: Codable {
    let renters: [Renter]
    
    enum CodingKeys: String, CodingKey {
        case renters = "people"
    }
}

struct Renter: Codable {
    let name: String
    let mobile: Int
    let hasPets: Bool
    let pets: [String]
    let address: Address
}

struct Address: Codable {
    let permanent, current: String
}

// Later on...
do {
    let decoder = JSONDecoder()
    let people: Tennants = try decoder.decode(Tennants.Type, 
                                              from: data)
    print(people)
} catch {
    Swift.debugPrint("Unable to decode response: \(error.localizedDescription)")
}

и просто print (или dump, .Swift.DebugPrint, Mirror(reflecting:)) в консоль, вы увидите что-то вроде этого:

Tennants(renters: [SwiftUI_Playgrounds.Renter(name: "David", mobile: 33333333, hasPets: true, pets: ["Dog", "Fish", "Bird"], address: SwiftUI_Playgrounds.Address(permanent: "France", current: "UK")), SwiftUI_Playgrounds.Renter(name: "Jordan", mobile: 33333333, hasPets: false, pets: [], address: SwiftUI_Playgrounds.Address(permanent: "Austrailia", current: "US")), SwiftUI_Playgrounds.Renter(name: "Peter", mobile: 33333333, hasPets: true, pets: ["Lizard"], address: SwiftUI_Playgrounds.Address(permanent: "India", current: "FR")), SwiftUI_Playgrounds.Renter(name: "Sarah", mobile: 33333333, hasPets: false, pets: [], address: SwiftUI_Playgrounds.Address(permanent: "Egypt", current: "US")), SwiftUI_Playgrounds.Renter(name: "Rory", mobile: 33333333, hasPets: true, pets: ["Snakes"], address: SwiftUI_Playgrounds.Address(permanent: "United Kingdom", current: "US"))])

В крайнем случае, это неплохо, и, безусловно, пригодно для использования. Но если вы поместите эти данные в DataFrame TabularData и выведете это? Что ж, давайте просто рискнем сказать, что это чуть приятнее. Гляньте на это:

Разве это не прекрасно?????

Я начал использовать эту технику, когда любые из этих пунктов совпадают:

  • Я имею дело с большими объемами данных

  • Независимо от размера, если я хочу отсортировать или отфильтровать данные

  • Мне нужен простой способ визуализации данных, которые я получаю из своего собственного слоя модели

  • Я просто хочу, чтобы читать это было проще

... и я обожаю это. Код для его запуска тривиален:

do {
    let people = try await loadPeople()
    let data = try JSONEncoder().encode(people.renters)

    // Create the DataFrame from .json data
    let dataFrame = try DataFrame(jsonData: data)

    // Beautiful print
    print(dataFrame.description(options: .init(maximumLineWidth: 250)))
} catch {
    Swift.debugPrint("Unable to create DataFrame: \(error.localizedDescription)")
}

Обратите внимание на параметр options:. Там вы можете управлять шириной ячейки, параметрами форматирования даты, шириной строк и многим другим. Если вы хотите, чтобы гора данных была быстро переведена в удобный вас формат, хорошим вариантом является создание одноразовых экземпляров DataFrame.

Если вы хотите, вы даже можете немного сократить данные, чтобы получить доступ к определенным свойствам. Например, что, если бы я только хотел знать, у кого из арендаторов были домашние животные и что это были за животные?

Это не проблема, так как вы можете получить другой DataFrame из существующего, но укажите, что он должен включать только несколько интересующих вас столбцов:

do {
    let people = try await loadPeople()
    let data = try JSONEncoder().encode(people.renters)

    // Create the DataFrame from .json data
    let dataFrame = try DataFrame(jsonData: data)

    // Just get names and pets
    let partialFrame = dataFrame.selecting(columnNames: "name", "pets")
    print(partialFrame)
} catch {
    Swift.debugPrint("Unable to create DataFrame: \(error.localizedDescription)")
}

// Results in...
┏━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃   ┃ name     ┃ pets                                            ┃
┃   ┃  ┃ >>                          ┃
┡━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ 0 │ David    │ [Optional(Dog), Optional(Fish), Optional(Bird)] │
│ 1 │ Jordan   │ []                                              │
│ 2 │ Peter    │ [Optional(Lizard)]                              │
│ 3 │ Sarah    │ []                                              │
│ 4 │ Rory     │ [Optional(Snakes)]                              │
└───┴──────────┴─────────────────────────────────────────────────┘
5 rows, 2 columns

Там также есть так много разновидностей функциональных возможностей Swift, созданных специально для фреймворка. Вы поняли идею, но теперь предположим, что вам нужны были только имена, и чтобы они были отсортированны:

do {
    let people = try await loadPeople()
    let data = try JSONEncoder().encode(people.renters)

    // Create the DataFrame from .json data
    let dataFrame = try DataFrame(jsonData: data)

    // Select only names, and sort them
    let sortedNames = dataFrame.sorted(on: .init("name", String.self), by: { lhs, rhs in
        lhs < rhs
    }).selecting(columnNames: "name")
    print(sortedNames.description)
} catch {
    Swift.debugPrint("Unable to create DataFrame: \(error.localizedDescription)")
}

// Results in...
┏━━━┳━━━━━━━━━━┓
┃   ┃ name     ┃
┃   ┃  ┃
┡━━━╇━━━━━━━━━━┩
│ 0 │ David    │
│ 1 │ Jordan   │
│ 2 │ Peter    │
│ 3 │ Rory     │
│ 4 │ Sarah    │
└───┴──────────┘
5 rows, 1 column

Опять же, здесь вы можете многое сделать. Вы можете указать значения по умолчанию, полностью преобразовать или объединить столбцы для декодирования ваших собственных моделей или даже распечатать статистические данные с помощью numericSummary.

Я сейчас уточняю суть, но практически нет ничего, что вы не могли бы сделать с увеличением или форматированием данных. В этом конкретном посте показано, как я лично использую DataFrame с консолью, но вы можете использовать его для гораздо более практичных целей. На самом деле, именно для этого он и предназначен.

Предположим, у вас есть два столбца, представляющие долготу и широту, возвращаемые из некоторого источника данных. Вы могли бы объединить их в combineColumns (into:transform), чтобы создать экземпляры CLLocation для использования, пока кто-то ищет местоположение в строке поиска. Или вы могли бы выполнить SQL-подобные объединения двух разных экземпляров DataFrame, используя joined(on:).

Прекрасно.

let concatenatedThoughts = """

There are several ways to spin up a DataFrame, too. You can use local .csv or .json files, or their raw data and more. Be sure to check out the docs."""

Честно говоря, фреймворк заслуживает отдельного поста. Если у вас есть какие-либо табличные данные (т.е. данные, структурированные или неструктурированные, в строках и столбцах), вы можете сортировать, изменять, фильтровать или просеивать их, используя их мощный (я знаю, это клише) API.

Если вы хотите повозиться с ним прямо сейчас, чтобы запачкать руки, просто создайте DataFrame, используя словарный литерал, а затем вы можете поиграть с выгрузкой на консоль, как мы сделали здесь, или пощупать его API:

let testFrame: DataFrame = [
    "id": [0, 1, 2, 3, 4],
    "job": ["developer", "designer", "pm", "em", "staff SWE"],
    "fillBy": ["jordan", "jansyn", "bennett", "remy", "baylor"]
]
        
print(testFrame.description)

// Results in...
┏━━━┳━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━┓
┃   ┃ id    ┃ job       ┃ fillBy   ┃
┃   ┃  ┃   ┃  ┃
┡━━━╇━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━┩
│ 0 │     0 │ developer │ jordan   │
│ 1 │     1 │ designer  │ jansyn   │
│ 2 │     2 │ pm        │ bennett  │
│ 3 │     3 │ em        │ remy     │
│ 4 │     4 │ staff SWE │ baylor   │
└───┴───────┴───────────┴──────────┘
5 rows, 3 columns

Этот пост довольно забавный, потому что он показывает вам, что если вы используете что-то немного не так, как указано в инструкции, иногда вы получаете полезные результаты.

Возможно, Cupertino & Friends™ не предполагали, что разработчики будут использовать фреймворк TabularData для... ведения журнала отладки? Это своего рода ситуация лингва франка, поскольку фреймворк, созданный для максимальной эффективности обучения моделей машинного обучения, пересекается и общается с людьми, которые просто хотят вывести “Это работает” в консоли.

Но все же мы здесь, и это невероятно полезно для нашей работы.

До скорого ✌

Комментарии (0)