Ответ 1
Это очень общий вопрос, но, насколько я могу судить, если вы хотите использовать некоторые методы ML, целесообразно сначала преобразовать данные в аккуратный формат данных.
Насколько я не могу сказать из документации, что @RootTwo приятно ссылается в своем комментарии, вы на самом деле имеете дело с двумя наборами данных: одним примером плоской таблицы и одной таблицей продуктов. (Вы можете позже присоединиться к двум, чтобы получить одну таблицу, если это необходимо.)
Давайте сначала создадим парсеры, которые декодируют разные строки в несколько информативную структуру данных:
Для строк с примерами мы можем использовать:
def process_example(example_line):
# example ${exID}: ${hashID} ${wasAdClicked} ${propensity} ${nbSlots} ${nbCandidates} ${displayFeat1}:${v_1}
# 0 1 2 3 4 5 6 7 ...
feature_names = ['ex_id', 'hash', 'clicked', 'propensity', 'slots', 'candidates'] + \
['display_feature_' + str(i) for i in range(1, 11)]
are_numbers = [1, 3, 4, 5, 6]
parts = example_line.split(' ')
parts[1] = parts[1].replace(':', '')
for i in are_numbers:
parts[i] = float(parts[i])
if parts[i].is_integer():
parts[i] = int(parts[i])
featues = [int(ft.split(':')[1]) for ft in parts[7:]]
return dict(zip(feature_names, parts[1:7] + featues))
Этот метод хакерский, но он выполняет свою работу: анализирует функции и приводит к числам, где это возможно. Вывод выглядит так:
{'ex_id': 20184824,
'hash': '57548fae76b0aa2f2e0d96c40ac6ae3057548faee00912d106fc65fc1fa92d68',
'clicked': 0,
'propensity': 1.416489e-07,
'slots': 6,
'candidates': 30,
'display_feature_1': 728,
'display_feature_2': 90,
'display_feature_3': 1,
'display_feature_4': 10,
'display_feature_5': 16,
'display_feature_6': 1,
'display_feature_7': 26,
'display_feature_8': 11,
'display_feature_9': 597,
'display_feature_10': 7}
Далее приведены примеры продуктов. Как вы упомянули, проблема заключается в множественном вхождении значений. Я думаю, что имеет смысл объединять уникальные пары "свойство-значение" по частоте. Информация не теряется, но она помогает нам кодировать аккуратный образец. Это должно ответить на ваш второй вопрос.
import toolz # pip install toolz
def process_product(product_line):
# ${wasProduct1Clicked} exid:${exID} ${productFeat1_1}:${v1_1} ...
parts = product_line.split(' ')
meta = {'label': int(parts[0]),
'ex_id': int(parts[1].split(':')[1])}
# extract feautes that are ${productFeat1_1}:${v1_1} separated by ':' into a dictionary
features = [('product_feature_' + str(i), int(v))
for i, v in map(lambda x: x.split(':'), parts[2:])]
# count each unique value and transform them into
# feature_name X feature_value X feature_frequency
products = [dict(zip(['feature', 'value', 'frequency'], (*k, v)))
for k, v in toolz.countby(toolz.identity, features).items()]
# now merge the meta information into each product
return [dict(p, **meta) for p in products]
который в основном извлекает метку и функции для каждого примера (пример для строки 40):
[{'feature': 'product_feature_11',
'value': 0,
'frequency': 1,
'label': 0,
'ex_id': 19168103},
{'feature': 'product_feature_12',
'value': 1,
'frequency': 1,
'label': 0,
'ex_id': 19168103},
{'feature': 'product_feature_13',
'value': 0,
'frequency': 1,
'label': 0,
'ex_id': 19168103},
{'feature': 'product_feature_14',
'value': 2,
'frequency': 1,
'label': 0,
'ex_id': 19168103},
{'feature': 'product_feature_15',
'value': 0,
'frequency': 1,
'label': 0,
'ex_id': 19168103},
{'feature': 'product_feature_17',
'value': 2,
'frequency': 2,
'label': 0,
'ex_id': 19168103},
{'feature': 'product_feature_21',
'value': 55,
'frequency': 2,
'label': 0,
'ex_id': 19168103},
{'feature': 'product_feature_22',
'value': 14,
'frequency': 1,
'label': 0,
'ex_id': 19168103},
{'feature': 'product_feature_22',
'value': 54,
'frequency': 1,
'label': 0,
'ex_id': 19168103},
{'feature': 'product_feature_24',
'value': 3039,
'frequency': 1,
'label': 0,
'ex_id': 19168103},
{'feature': 'product_feature_25',
'value': 721,
'frequency': 1,
'label': 0,
'ex_id': 19168103},
{'feature': 'product_feature_33',
'value': 386,
'frequency': 2,
'label': 0,
'ex_id': 19168103},
{'feature': 'product_feature_35',
'value': 963,
'frequency': 1,
'label': 0,
'ex_id': 19168103}]
Итак, когда вы обрабатываете ваш поток построчно, вы можете решить, отображать ли пример или продукт:
def process_stream(stream):
for content in stream:
if 'example' in content:
yield process_example(content)
else:
yield process_product(content)
Я решил сделать генератор здесь, потому что он будет полезен для обработки данных функциональным способом, если вы решите не использовать pandas
. В противном случае сжатие списка будет
будь жареным.
Теперь для забавной части: мы читаем строки из данного (примера) URL-адреса одну за другой и
назначьте их в соответствующие им наборы данных (пример или продукт).
Я буду использовать reduce
здесь, потому что это весело :-). Я не буду вдаваться в подробности того, что на самом деле делает map/reduce
(это ваше дело). Вместо этого вы всегда можете использовать простой цикл for.
import urllib.request
import toolz # pip install toolz
lines_stream = (line.decode("utf-8").strip()
for line in urllib.request.urlopen('http://www.cs.cornell.edu/~adith/Criteo/sample.txt'))
# if you care about concise but hacky approach you could do:
# blubb = list(toolz.partitionby(lambda x: 'hash' in x, process_file(lines_stream)))
# examples_only = blubb[slice(0, len(blubb), 2)]
# products_only = blubb[slice(1, len(blubb), 2)]
# but to introduce some functional approach lets implement a reducer
def dataset_reducer(datasets, content):
which_one = 0 if 'hash' in content else 1
datasets[which_one].append(content)
return datasets
# and process the stream using the reducer. Which results in two datasets:
examples_dataset, product_dataset = toolz.reduce(dataset_reducer, process_stream(lines), [[], []])
Отсюда вы можете преобразовать свои наборы данных в аккуратный фрейм данных, который вы можете использовать для применения машинного обучения. Остерегайтесь NaN
/пропущенных значений, распределений и т.д. Вы можете объединить два набора данных с merge
, чтобы получить одну большую плоскую таблицу образцов X объектов. Тогда вы сможете более или менее использовать разные методы, например, scikit-learn
.
import pandas
examples_dataset = pandas.DataFrame(examples_dataset)
product_dataset = pandas.concat(pandas.DataFrame(p) for p in product_dataset)
Примеры набора данных
candidates clicked ... propensity slots
0 30 0 ... 1.416489e-07 6
1 23 0 ... 5.344958e-01 3
2 23 1 ... 1.774762e-04 3
3 28 0 ... 1.158855e-04 6
Набор данных продукта (product_dataset.sample(10)
)
ex_id feature frequency label value
6 10244535 product_feature_21 1 0 10
9 37375474 product_feature_25 1 0 4
6 44432959 product_feature_25 1 0 263
15 62131356 product_feature_35 1 0 14
8 50383824 product_feature_24 1 0 228
8 63624159 product_feature_20 1 0 30
3 99375433 product_feature_14 1 0 0
9 3389658 product_feature_25 1 0 43
20 59461725 product_feature_31 8 0 4
11 17247719 product_feature_21 3 0 5
Помните о product_dataset
. Вы можете "поворачивать" свои объекты в строках в виде столбцов (см. изменение формы документов).