pandas извлекать текст в скобках и создавать строки для каждого бита текста - PullRequest
3 голосов
/ 04 апреля 2020

В кадре данных pandas мне нужно извлечь текст в квадратных скобках и вывести этот текст в виде нового столбца. Мне нужно сделать это на уровне «StudyID» и создать новые строки для каждого извлеченного бита текста.

Вот упрощенный пример кадра данных

data = {
    "studyid":['101', 
                '101', 
                '102', 
                '103'],
    "Question":["Q1",
                "Q2",
                "Q1",
                "Q3"],
    "text":['I love [Bananas] and also [oranges], and [figs]',
            'Yesterday I ate [Apples]',
            '[Grapes] are my favorite fruit',
            '[Mandarins] taste like [oranges] to me'],
}
df2 = pd.DataFrame(data)

Я нашел решение (см. Приведенный ниже код, если вы запустите это, он показывает желаемый результат), однако он очень длинный со многими шагами. Я хочу знать, есть ли более короткий способ сделать это.

Вы увидите, что я использовал str.findall () для регулярного выражения, но я первоначально попытался str.extractall (), который выводит извлеченный текст в информационный фрейм, но я не знал, как вывести извлеченный извлеченный текст со столбцами «studyid» и «question», включенных в фрейм данных, созданный extractall (). Поэтому я прибег к использованию str.findall ().

Вот мой код («Я знаю, что он неуклюжий») - как я могу уменьшить количество шагов? Заранее благодарим за помощь!

 # Step 1: Use Regex to pull put the text between the square brackets
df3 = pd.DataFrame(df2['text'].str.findall(r"(?<=\[)([^]]+)(?=\])").tolist())

  # Step 2: Merge the extracted text back with the original data
df3 = df2.merge(df3, left_index=True, right_index=True)

  # Step 3: Transpose the wide file to a long file (e.g. panel)
df4 = pd.melt(df3, id_vars=['studyid', 'Question'], value_vars=[0, 1, 2])

  # Step 4: Delete rows with None in the value column
indexNames = df4[df4['value'].isnull()].index
df4.drop(indexNames , inplace=True)

  # Step 5: Sort the data by the StudyID and Question
df4.sort_values(by=['studyid', 'Question'], inplace=True)

  # Step 6: Drop unwanted columns
df4.drop(['variable'], axis=1, inplace=True)

  # Step 7: Reset the index and drop the old index
df4.reset_index(drop=True, inplace=True)

df4

Ответы [ 2 ]

1 голос
/ 04 апреля 2020

Если для столбца возможно присвоить обратный вывод Series.str.findall, используйте DataFrame.explode, используется последний для уникального индекса DataFrame.reset_index с drop=True:

df2['text'] = df2['text'].str.findall(r"(?<=\[)([^]]+)(?=\])")

df4 = df2.explode('text').reset_index(drop=True)

Решение с Series.str.extractall, удаленным вторым уровнем MultiIndex и последним использованием DataFrame.join для добавления к оригиналу:

s = (df2.pop('text').str.extractall(r"(?<=\[)([^]]+)(?=\])")[0]
                   .reset_index(level=1, drop=True)
                   .rename('text'))

df4 = df2.join(s).reset_index(drop=True)

print (df4)
  studyid Question       text
0     101       Q1    Bananas
1     101       Q1    oranges
2     101       Q1       figs
3     101       Q2     Apples
4     102       Q1     Grapes
5     103       Q3  Mandarins
6     103       Q3    oranges
0 голосов
/ 04 апреля 2020

Вы можете «сжать» свой код до одиночной инструкции:

df2[['studyid', 'Question']].join(df2['text'].str.findall(
    r'\[([^]]+)\]').explode().rename('value'))

Даже регулярное выражение может быть упрощено: нет необходимости смотреть назад или смотреть вперед. Просто поместите обе скобки перед / после группы захвата.

Если вам нужно, сохраните этот результат в переменной (например, df4 = ... ).

Примечание: В своем решении вы назвали последний столбец в конечном результате ( df4 ) как значение , поэтому я повторил его в своем решении. Но если вы хотите изменить это имя на любое другое, то вместо sh замените 'value' в моем решении другим именем по вашему выбору.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...