Как преобразовать столбцы CSV во входной файл Vowpal Wabbit txt

#python #vowpalwabbit

#python #vowpalwabbit

Вопрос:

Я пытаюсь преобразовать csv (с 10 столбцами) в файл формата ввода vowpal wabbit txt. Некоторые столбцы csv содержат целочисленные значения, а некоторые — строки (например: com.12346.xyz). Например, если мои столбцы csv выглядят так:

 loss       weight         SSD_id      weight    label      imp            feat_val
0.693147   0.693147            1         1.0  -1.0000   0.0000       com.12346.xyz
0.419189   0.145231            2         2.0   1.0000  -1.8559       com.12346.xyz
0.235457   0.051725            4         4.0  -1.0000  -2.7588       com.12356.xyz
6.371911   12.508365           8         8.0  -1.0000  -3.7784       com.12346.xyz
3.485084   0.598258           16        16.0   1.0000  -2.2767       com.12346.xyz
1.765249   0.045413           32        32.0  -1.0000  -2.8924       com.1236.xyz
1.017911   0.270573           64        64.0  -1.0000  -3.0438       com.12236.xyz
0.611419   0.204927          128       128.0   1.0000  -3.1539       com.16746.xyz
0.469127   0.326834          256       256.0  -1.0000  -1.6101       com.1946.xyz
0.403473   0.337820          512       512.0   1.0000  -2.8843       com.126.xyz
0.337348   0.271222         1024      1024.0  -1.0000  -2.5209       com.1346.xyz
0.328909   0.320471         2048      2048.0   1.0000  -2.0732       com.1234.xyz
0.309401   0.289892         4096      4096.0   1.0000  -2.7639       com.12396.xyz
 

и формат ввода vowpal wabbit выглядит следующим образом:

 label weight |i imp SSD_id loss |c feat_val
 

и внутри значений файла vowpal wabbit txt должно быть:

 -1 0.051725 |i imp:-2.7588 SSD_id:4 loss:0.235457 |c feat_val=com.12356.xyz
 1 0.598258 |i imp:-2.7588 SSD_id:4 loss:3.485034 |c feat_val=com.12346.xyz
 

… и так далее… для всех значений строк. У меня есть огромное количество строк в файле csv, которые я хочу преобразовать в указанный выше формат и сохранить их все в одном файле txt. Я начал с этой небольшой функции, приведенной ниже:

 def to_new_format(document, label=None):
      return str(label or '')   ' |i '   ' '.join(re.findall('w{3,}', document.lower()))   'n'
to_new_format(str(text_train[1])
 

но теперь я полностью потерян после многих попыток с фреймом данных, форматированием csv и попытками выполнения функций. Может кто-нибудь дать мне какое-то указание, как я могу добиться этого за минимальные строки кода.

Ответ №1:

Это проще сделать, чем может показаться, из-за некоторых удобных способов, которыми Pandas позволяет обрабатывать последовательности почти так же, как отдельные значения в Python.

Сначала мы импортируем ваш CSV-файл, обрабатывая все значения как строки, чтобы упростить форматирование:

 import pandas as pd

df = pd.read_csv('test_data.txt', dtype=pd.StringDtype())
 

Ваш label столбец записывается так же, как 1.0000 в вашем файле, но вы не хотите, чтобы в вашем выводе были десятичные числа или нули. Мы можем исправить это с помощью str.replace метода Pandas.

 df.label = df.label.str.replace('.0000', '', regex=False)
 

И вот волшебная часть: мы можем объединить их так же, как если бы они были отдельными строками!

 formatted = (
    df.label   ' '   df.weight  
    ' |i imp:'   df.imp  
    ' SSD_id: '   df.SSD_id  
    ' loss:'   df.loss  
    ' |c feat_val='   df.feat_val  
    'n'
)
 

Этот код выглядит так, будто он создает строку, но из-за того, как он включает столбцы фрейма данных (каждый из которых представляет собой последовательность Pandas), результатом также является последовательность:

 print(formatted)

0     -1 0.693147 |i imp:0.0000 SSD_id: 1 loss:0.693...
1     1 0.145231 |i imp:-1.8559 SSD_id: 2 loss:0.419...
2     -1 0.051725 |i imp:-2.7588 SSD_id: 4 loss:0.23...
3     -1 12.508365 |i imp:-3.7784 SSD_id: 8 loss:6.3...
4     1 0.598258 |i imp:-2.2767 SSD_id: 16 loss:3.48...
5     -1 0.045413 |i imp:-2.8924 SSD_id: 32 loss:1.7...
6     -1 0.270573 |i imp:-3.0438 SSD_id: 64 loss:1.0...
7     1 0.204927 |i imp:-3.1539 SSD_id: 128 loss:0.6...
8     -1 0.326834 |i imp:-1.6101 SSD_id: 256 loss:0....
9     1 0.337820 |i imp:-2.8843 SSD_id: 512 loss:0.4...
10    -1 0.271222 |i imp:-2.5209 SSD_id: 1024 loss:0...
11    1 0.320471 |i imp:-2.0732 SSD_id: 2048 loss:0....
12    1 0.289892 |i imp:-2.7639 SSD_id: 4096 loss:0....
 

При такой печати каждая строка усекается, но все это есть. Например:

 print(formatted[0])

-1 0.693147 |i imp:0.0000 SSD_id: 1 loss:0.693147 |c feat_val=com.12346.xyz
 

Все, что осталось, это сохранить его в файл:

 with open('out.txt', 'w') as f:
    f.writelines(formatted)
 

Комментарии:

1. ваше решение очень чистое, выдающееся и простое для понимания. Я узнал кое-что новое. Я пробовал это с одной недели. Да, вы правы, это выглядит сложно, но просто реализовать, если все сделано правильно. Если данные поступают в формате json, будет ли это все еще действительным?

2. Рад, что это помогает! Честно говоря, я сейчас тоже изучаю Pandas, и ответ на этот вопрос был для меня познавательным. Так уж получилось, что у Pandas read_json тоже есть метод! Возможно, вам придется что-то изменить, в зависимости от того, как именно отформатирован ваш файл, но да, этот подход все равно должен работать.

3. Я попробую с помощью read_json и опубликую здесь, если это сработает. Данные в основном поступают в формате json при отправке запроса, но для меня было слишком сложно найти какое-либо решение, поэтому я решил сначала попробовать с csv. Да, ваш подход к решению отлично работает с тем, что я пытался сделать с csv, и теперь попробую с json.

Ответ №2:

В последних версиях Vowpal Wabbit имеет модуль Python, который упрощает преобразование pandas DataFrame в формат ввода Vowpal Wabbit.

Например, используя версию 8.11.0 и предполагая, что ваши типы столбцов следующие :

 loss        float64
weight      float64
SSD_id        int64
weight.1    float64
label         int64
imp         float64
feat_val     object
dtype: object
 

вы могли бы использовать следующий код

 from vowpalwabbit.DFtoVW import DFtoVW, SimpleLabel, Namespace, Feature

label = SimpleLabel(label="label", weight="weight")
namespaces = [
    Namespace(features=[Feature(col) for col in ["imp", "SSD_id", "loss"]], name="i"),
    Namespace(features=Feature("feat_val"), name="c")
]

converter = DFtoVW(df=dat, label=label, namespaces=namespaces)

 

затем вызовите метод convert_df для получения выходных данных

 examples = converter.convert_df()

print(examples)

['-1 0.693147 |i imp:0.0 SSD_id:1 loss:0.693147 |c feat_val=com.12346.xyz',
 '1 0.145231 |i imp:-1.8559 SSD_id:2 loss:0.419189 |c feat_val=com.12346.xyz',
 '-1 0.051725 |i imp:-2.7588 SSD_id:4 loss:0.235457 |c feat_val=com.12356.xyz',
 '-1 12.508365 |i imp:-3.7784 SSD_id:8 loss:6.371911 |c feat_val=com.12346.xyz',
 '1 0.598258 |i imp:-2.2767 SSD_id:16 loss:3.485084 |c feat_val=com.12346.xyz',
 '-1 0.045413 |i imp:-2.8924 SSD_id:32 loss:1.765249 |c feat_val=com.1236.xyz',
 '-1 0.270573 |i imp:-3.0438 SSD_id:64 loss:1.017911 |c feat_val=com.12236.xyz',
 '1 0.204927 |i imp:-3.1539 SSD_id:128 loss:0.611419 |c feat_val=com.16746.xyz',
 '-1 0.326834 |i imp:-1.6101 SSD_id:256 loss:0.469127 |c feat_val=com.1946.xyz',
 '1 0.33782 |i imp:-2.8843 SSD_id:512 loss:0.403473 |c feat_val=com.126.xyz',
 '-1 0.271222 |i imp:-2.5209 SSD_id:1024 loss:0.337348 |c feat_val=com.1346.xyz',
 '1 0.320471 |i imp:-2.0732 SSD_id:2048 loss:0.328909 |c feat_val=com.1234.xyz',
 '1 0.289892 |i imp:-2.7639 SSD_id:4096 loss:0.309401 |c feat_val=com.12396.xyz']
 

и записать его в файл

 with open("test.dat", "w") as f:
    for example in examples:
        f.writelines(example "n")