Дерево — одна из популярных структур хранения и передачи данных. Универсальные форматы обмена, такие как JSON и XML, используют именно иерархическое представление информации. Однако большинство алгоритмов рассчитаны на то, что анализируемые данные хранятся в виде плоских таблиц. В Loginom имеются специальные компоненты для работы с древовидными структурами.
Самыми популярными структурами для хранения и передачи данных являются плоская таблица и дерево. В плоских таблицах информация представлена в виде простой конструкции, где строка является одним объектом или событием, а столбец — атрибутом, который описывает каждую запись. Дерево устроено сложнее — данные в нем хранятся в виде иерархии произвольной глубины вложенности.
Наиболее популярными форматами представления древовидных данных являются JSON и XML. Они хранят информацию в виде пар «ключ-значение», упорядоченных списков и других объектов, могут включать вложенные структуры. Эти форматы независимы от какой-либо платформы, операционной системы или базы данных.
Деревья обеспечивают большую гибкость, позволяют описывать сложные объекты, не имеют ограничений на количество атрибутов. Однако большинство алгоритмов анализа данных ориентированы на обработку более простых структур — плоских таблиц.
Файлы в формате JSON используются для обмена данными между приложениями, включая базы данных, аналитические платформы. Например, кредитные бюро предоставляют банкам доступ к кредитной истории заемщиков через API, основанном на JSON.
Кредитная история содержит большой объем сложно структурированной информации: данные о человеке, сведения о месте работы и ранее выданных кредитах, история платежей и многое другое. В формате JSON это может выглядеть так:
{
"applicant": {
"name": "Job Pou",
"birthdate": "1980-05-15",
"address": "123 Main St, City, State",
"employment": "ТТТ",
"annual_income": 600000
},
"credit_scores": {
"equifax": 750,
"transunion": 720,
"experian": 730
},
"credit_history": [
{
"account_type": "Credit Card",
"account_number": "123456789",
"current_balance": 50000,
"credit_limit": 100000,
"payment_status": "Current"
},
{
"account_type": "Auto Loan",
"account_number": "987654321",
"current_balance": 15000,
"original_balance": 20000,
"payment_status": "Late"
}
]
}
В этом примере содержится следующая информация:
Данная структура небольшая, но даже в ней имеется массив кредитной истории: Credit Card и Auto Loan. В реальности подобный файл обычно хранит намного больше информации и может содержать тысячи строк.
Заявка в виде дерева подается на вход системе принятия решений — кредитному конвейеру, который выполняет сложные проверки и формирует окончательный ответ. В процессе обработки иерархия может обогащаться новыми данными, такими как условия договора, процентная ставка, сумма кредита и т.п. Обычное для аналитических алгоритмов представление в виде плоской таблицы не способно отразить подобную сложную «древовидную» структуру.
Loginom предоставляет механизмы, которые позволяют обрабатывать древовидные структуры, преобразовывать их в таблицы и обратно.
Древовидная структура представляет собой организацию данных в виде иерархии, где каждый элемент, кроме корневого, имеет связь с родительским элементом. Также есть множество дочерних.
Большинство компонентов платформы работают с плоскими таблицами, а при необходимости обрабатывать иерархические данные можно воспользоваться двумя специальными компонентами, позволяющими преобразовать дерево в таблицу и обратно.
Таким образом, деревья трансформируются в данные, которые можно обработать при помощи стандартных механизмов, встроенных в Loginom. Либо провести обратную операцию — преобразовать итоги расчетов в иерархическую структуру для обмена информацией со сторонними системами.
В статье на конкретном примере рассматривается преобразование таблицы в дерево и обратно. Вся работа происходит в аналитической платформе Loginom (скачать бесплатную версию Loginom Community).
Для успешного усвоения материала предлагаем скачать сценарий и выполнить все шаги.
При импорте XML-файла данные автоматически трансформируются в дерево. Написания кода для этого не требуется, т.к. структура описывается в WSDL-файле. Файл с описанием структуры должен обязательно присутствовать.
C файлами JSON другая ситуация. Наличие файла с описанием структуры не обязательно. В этом случае автоматический разбор невозможен. Но эту проблему можно решить с помощью компонента JavaScript.
Входной JSON выглядит следующим образом:
Работа с такими данными неудобна, информацию можно привести к табличному виду с помощью компонента JavaScript. Для этого нужно написать небольшой скрипт, в котором преобразуется вложенная структура JSON в плоский формат таблицы, где каждая строка представляет один элемент данных, а столбцы таблицы соответствуют полям данных.
import { InputTable, InputTables, InputVariables, OutputTable, DataType, DataKind, UsageType } from "builtIn/Data";
const json = JSON.parse(InputTable.Get(0,'RawJSON'))
const fields = JSON.parse(InputVariables.Items.fields.Value)
let fieldsMap = {}
let globalIndexesCounters = {}
OutputTable.ClearColumns;
fields.forEach(field => {
OutputTable.AddColumn(field)
fieldsMap[field.DisplayName] = field.Name
if(field.Name.includes('_global_index')){
globalIndexesCounters[field.Name] = 0;
}
})
function AddRow(row){
OutputTable.Append()
Object.keys(row).forEach(key=>{
OutputTable.Set(key, row[key])
})
}
let rows = []
function getObjType(obj){
if(!obj || typeof obj !== 'object'){
return 'key'
} else {
if(Array.isArray(obj)){
return 'array'
} else {
return 'object'
}
}
}
function objWalk(obj, objKeyName, parentKeyPath, currentRow){
let objKeyPath = parentKeyPath ? parentKeyPath + '.' + objKeyName : objKeyName
if(Array.isArray(obj)){
let rowOfArr = Object.assign({},currentRow);
if(obj.length > 0){
const itemType = getObjType(obj[0])
switch(itemType){
case 'key':
let fieldIndex = fieldsMap[objKeyPath + '.' + objKeyName + '_global_index']
let field = fieldsMap[objKeyPath + '.' + objKeyName]
if(fieldIndex && field){
obj.forEach((item,idx) => {
rowOfArr[fieldIndex] = globalIndexesCounters[fieldIndex]++;
rowOfArr[field] = item
rows.push(Object.assign({},rowOfArr))
rowOfArr = Object.assign({},currentRow)
});
}
break;
case 'object':
obj.forEach((item,idx) => {
let fieldIndex = fieldsMap[objKeyPath + '.' + objKeyName + '_global_index']
if(fieldIndex){
rowOfArr[fieldIndex] = globalIndexesCounters[fieldIndex]++;
objWalk(item, objKeyName, parentKeyPath, rowOfArr)
rows.push(Object.assign({},rowOfArr))
rowOfArr = Object.assign({},currentRow);
}
});
break;
case 'array':
let fieldIndexA = fieldsMap[objKeyPath + '.' + objKeyName + '_global_index']
if(fieldIndexA){
obj.forEach((item,idx) => {
rowOfArr[fieldIndexA] = globalIndexesCounters[fieldIndexA]++;
objWalk(item, objKeyName+ '_SUBARR', objKeyPath, rowOfArr)
rows.push(Object.assign({},rowOfArr))
rowOfArr = Object.assign({},currentRow);
});
}
break;
}
}
}
else {
let keysByType = {
'key': [],
'object': [],
'array': []
}
Object.keys(obj).forEach(key => {
keysByType[getObjType(obj[key])].push(key)
})
keysByType.key.forEach(key => {
let keyPath = objKeyPath ? objKeyPath + '.' + key : key
let fieldName = fieldsMap[keyPath]
if(fieldName){
currentRow[fieldName] = obj[key]
}
})
keysByType.object.forEach(key => {
objWalk(obj[key], key, objKeyPath, currentRow)
})
keysByType.array.forEach(key => {
objWalk(obj[key], key, objKeyPath, currentRow)
})
}
}
let currentRow = {}
if(getObjType(json) == 'array'){
json.forEach((item,idx) => {
currentRow = { _global_index: idx}
objWalk(item, '', null,currentRow)
})
} else {
objWalk(json, '', null,currentRow)
}
if(rows.length == 0){
rows.push(currentRow)
}
rows.forEach(row => {
AddRow(row)
})
Получим следующую таблицу:
Таблица состоит из 4 строк и 30 столбцов. Имеются дубли и пропуски.
Т.к. присутствует адрес регистрации и фактический, в табличном виде добавляется столбец с индексом, появились строки «null», фактический адрес, регистрационный адрес.
Задача сводится к получению таблицы, состоящей из одной строки со значениями. В таком виде будет удобнее проводить дальнейший анализ.
Можно выделить уровни, которые представляют ветви дерева:
После этого соединить в одну таблицу, тогда ее отображение будет без пустых значений, дублей.
Существует несколько вариантов решения, включая комбинированное использование компонентов: Группировка, Фильтр строк, Параметры полей.
Этот подход вызывает определенные сложности:
Альтернативное решение — это применение компонентов Таблица в дерево и Дерево в таблицу.
Переведем таблицу в древовидную структуру с помощью компонента «Таблица в дерево».
Есть два варианта проставления связей:
При ручной настройке нужно создать главный узел «Person». При этом указать, что узел является «Контейнером». Это дает возможность создавать от него дочерние узлы.
От «Person» надо создать узлы с личной информацией, добавить узел «Document» с пометкой контейнер, чтобы добавлять в него узлы.
При создании узла «Addresses» нужно добавить пометку «Массив», т.к. в него вложен массив с объектами.
При ручном соединении необходимо проставить связь «Addresses_global_index» с «#Глобальный индекс».
Ручное соединение
Далее следует устанавливать связи. Их можно связать автоматически или проставить вручную.
Настройка узла «Таблица в дерево»
В результате получится следующая древовидная структура данных.
Древовидная структура данных
Такая структура корректно конвертируется в JSON и XML файлы.
Далее с помощью компонента «Дерево в таблицу» разделим данные на 3 таблицы, к которым можно применить различные алгоритмы.
В настройках указываются только нужные узлы. При их выборе можно использовать фильтрацию для быстрого поиска. А если выбрать узлы, которые имеют разных родителей, то получится таблица с пустыми значениями, т.е. придем к изначальному варианту.
Настройка компонента «Дерево в таблицу»
Таблица с паспортными данными, извлеченными из дерева, выглядит следующим образом.
Таблица с паспортными данными клиента
Для корректного отображения адреса при помощи Калькулятора можно собрать нужную строку concat( "Российская федерация, ",Region + ", ", City + ", ", Street + ", ", "д. " + House + ", ","к. " + Korpus + ", ","кв. " + Building)
Таблица с адресом
С помощью компонента Соединение получается преобразованная таблица, консолидирующая нужную информацию, с которой можно проводить анализ.
Сценарий на платформе Loginom
Древовидные структуры позволяют разбивать данные на более мелкие категории и анализировать их в контексте иерархии. Подобный подход часто используется при предоставлении данных о финансовых транзакциях, счетах, клиентах и других операциях.
Древовидная структура данных, содержащая вложенные структуры, корректно конвертируется в JSON и XML. Данные правильно отображают сложные иерархические отношения.
Плюсы использования древовидной структуры данных на платформе Loginom:
Минусы:
Большинство алгоритмов анализа не приспособлены для обработки иерархических данных. Наличие компонентов, позволяющих преобразовать дерево в таблицу и обратно, позволяет, с одной стороны, принимать и отдавать сторонним системам иерархическую информацию, а с другой — применять в полном объеме все алгоритмы анализа, встроенные в Loginom.
Чтобы оптимизировать работу с JSON-файлами в Loginom пройдите курс «Эффективная работа с JSON».
Другие материалы по теме: