Генерация большого количества предложений на основе частоты ввода - PullRequest
0 голосов
/ 11 октября 2018

Моя цель состоит в том, чтобы генерировать предложения на основе частоты ввода.Например, у меня есть такой ввод:

>>> df = pd.DataFrame({"s":["a", "a", "b", "b", "c", "c"], "m":[["x", "y"], ["x", "z"], ["y", "w", "z"], ["y"], ["z"], ["z"]]})
>>> df.set_index("s")
>>> df
           m
s
a     [x, y]
a     [x, z]
b  [y, w, z]
b        [y]
c        [z]
c        [z]

Я хочу иметь функцию gen_sentence(s), которая принимает s и генерирует случайное непустое предложение на основе частоты букв в столбце m.Поэтому gen_sentence("a") должен генерировать предложения, в которых все они будут содержать x, 50% из них должны содержать y и 50% z.

Моя интуиция заключается в том, чтобы преобразовать DataFrame в DataFrameчастота, так что для примера что-то вроде этого:

     w  x    y    z
s
a  0.0  1  0.5  0.5
b  0.5  0  1.0  0.5
c  0.0  0  0.0  1.0

И затем бросить случайное число для каждого столбца с учетом s:

def gen_sentence(fdf, s):
    return fdf.columns[np.random.random(len(fdf.columns)) < fdf.loc[s]]

Однако у меня естьне знаю, как преобразовать DataFrame в частоту DataFrame.

Решение, вероятно, будет заключаться в использовании df.agg["s"], но какую функцию я применяю к агрегату?

На самом деленабор данных довольно большой, более 1 миллиона строк, около 500 различных слов в m и около 100 различных значений для s, а таблица частот будет разреженной: большинство s имеют нулевую частоту для большинства слов вm.Кроме того, мне нужно сгенерировать как минимум пару сотен тысяч предложений, поэтому я пытаюсь найти реализацию, которая может генерировать предложение как можно быстрее.Кроме того, решение не должно использовать Panda, я просто думал, что векторизованная реализация большинства его функций является самым быстрым решением.


Итак, кратко , сначалаКак преобразовать DataFrame в частоту DataFrame и, во-вторых, есть ли более быстрый способ генерации предложений?


Я проверил свою реализацию, чтобы убедиться, что она достаточно быстрая и приемлемая: частотаDataFrame с 500 строками и 100 столбцами может генерировать 5000 предложений примерно за 1,2 секунды на моем компьютере.

Если вы хотите проверить свой собственный метод на моем, вот мой тест:

import timeit

setup = '''
import pandas as pd
import numpy as np

def val():
    v = np.random.normal(0, 0.2)
    return v if 0 <= v <= 1 else 0


def gen_sentence(fdf, s):
    return fdf.columns[np.random.random(len(fdf.columns)) < fdf.loc[s]]

n = 500
m = 100
fdf = pd.DataFrame([[val() for _ in range(n)] for _ in range(m)])
fdf = fdf.join(pd.DataFrame({"s": [i for i in range(m)]}))
fdf = fdf.set_index("s")
fdf.columns = ["w%d" % i for i in range(n)]
'''

test = "x = np.random.randint(0, m); gen_sentence(fdf, x)"

print(timeit.timeit(test, setup=setup, number=5000))

Ответы [ 2 ]

0 голосов
/ 15 октября 2018

С помощью Аллы Таригати у меня теперь есть решение для первой части моего вопроса:

letters = set(x for l in df["m"] for x in l)
for letter in letters:
    df.loc[:, letter] = df.m.apply(lambda x: letter in x)

df = df.drop(["m"], axis=1)

gdf = df.groupby("s")
fdf = gdf.agg(lambda x: sum(x))
fdf = fdf.divide(gdf.size(), axis="index")
print(fdf)

вывод:

     y    x    z    w
s                    
a  0.5  1.0  0.5  0.0
b  1.0  0.0  0.5  0.5
c  0.0  0.0  1.0  0.0

Обратите внимание, что в третьей строке я измениллямбда-функция до letter in x, чтобы дублирующие буквы в предложении не учитывались несколько раз.

И, как и у Аллы Таригати, это не очень быстрое решение, поэтому улучшения приветствуются!

0 голосов
/ 11 октября 2018

Для преобразования в частотный фрейм данных попробуйте это (не лучшее решение, но оно работает):

for letter in ['x', 'y', 'w', 'z']:
    df.loc[:, letter] = df.m.apply(lambda x: x.count(letter))

df = df.drop(['m'], axis=1)

df_1 = df.groupby('s').agg(lambda x: sum(x)).reset_index()

print(df_1)

Вывод:

   s  x  y  w  z
0  a  2  1  0  1
1  b  0  2  1  1
2  c  0  0  0  2

Другая альтернатива (без for loop, используя stack и pivot_table):

import numpy as np
df_1 = (df.m.apply(pd.Series).stack().to_frame('m')).reset_index().set_index('level_0')['m']
df_1 = pd.concat([df['s'], df_1], axis=1).reset_index()[['s', 'm']]
df_1.insert(1, 'freq', 1)
df_1 = pd.pivot_table(df_1, values='freq', index='s', columns='m', aggfunc=np.sum).fillna(0)
df_1 = df_1.div(df_1.max(axis=1), axis=0)
df_1.columns.name=None

print(df_1)

Выход:

     w    x    y    z
s                    
a  0.0  1.0  0.5  0.5
b  0.5  0.0  1.0  0.5
c  0.0  0.0  0.0  1.0
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...