Условное слияние строк данных

У меня есть фрейм данных 2xN сообщений чата, и я пытаюсь найти самый чистый способ объединить последовательные сообщения, исходящие от одного и того же динамика. Вот пример данных, с которыми я работаю:

mydata = pd.DataFrame(data=[['A','random text'],
                            ['B','random text'],
                            ['A','random text'],
                            ['A','random text'],
                            ['A','random text'],
                            ['B','random text'],
                            ['A','random text'],
                            ['B','random text'],
                            ['B','random text'],
                            ['A','random text']], columns=['speaker','message'])

Надеюсь, вы видите, что порядок динамиков не в формате ABAB, как мне бы хотелось. Вместо этого есть несколько последовательностей AAAB и ABBA. Мое текущее мышление состоит в том, чтобы перестроить фрейм данных с нуля, проверяя идентификатор каждой строки с идентификатором следующей позиции индекса...

mergeCheck = True
while mergeCheck is True:
    # set length of the dataframe
    lenDF = len(mydata)
# empty list to rebuild dataframe
mergeDF = []
# set index position at the beginning of dataframe
i = 0            
while i < lenDF-1:
   # check whether adjacent rows have different ID
   if mydata['speaker'].iloc[i] != mydata['speaker'].iloc[i+1]:
       # if true, append row as is to mergeDF list
       mergeDF.append([mydata['speaker'].iloc[i],
                       mydata['message'].iloc[i]])
       # increase index position by 1
       i +=1
   else:
       # merge messages
       mergeDF.append([mydata['speaker'].iloc[i],
                       mydata['message'].iloc[i] + mydata['message'].iloc[i+1]])
       # increase index position by 2
       i +=2
# exit the loop if index position falls on the last message
if i == lenDF-1: 
    # if true, append row as is to mergeDF list
    mergeDF.append([mydata['speaker'].iloc[i],
                    mydata['message'].iloc[i]])
    # increase counter by 1
    i +=1
if i == lenDF:
    mergeCheck = False

Однако это работает только для двух соседних сообщений. Возвращаясь к моим исходным данным, при помещении в фрейм данных вышеуказанная функция генерирует следующий вывод...

--------------------------
  speaker  |   message
--------------------------
    A         'random text'
    B         'random text'
    A         'random textrandom text'
    A         'random text'
    B         'random text'
    A         'random text'
    B         'random textrandom text'
    A         'random text'
--------------------------

Я подумал расширить функцию, чтобы проверить больше сравнений i (т.е. делает ли '.iloc[i] != .iloc[i+2]' или '.iloc[i] != .iloc[i+3]' д.), но это очень быстро становится неработоспособным. Я думаю, что мне нужно каким-то образом повторить вышеуказанную функцию, пока фрейм данных не будет в нужном формате. Но я не уверен, как это сделать.

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
0
52
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Возможное решение таково:

df1 = mydata[mydata['speaker']=='A'].reset_index()
df2= mydata[mydata['speaker']=='B'].reset_index()
df = pd.concat([df1, df2]).sort_index()

который возвращает

  index speaker      message
0      0       A  random text
0      1       B  random text
1      2       A  random text
1      5       B  random text
2      3       A  random text
2      7       B  random text
3      4       A  random text
3      8       B  random text
4      6       A  random text
5      9       A  random tex

если у вас есть метка времени для них, не забудьте отсортировать по времени/дате перед сбросом индекса. Кроме того, при объединении остерегайтесь времени.

РЕДАКТИРОВАТЬ

После ваших разъяснений в комментариях, я предлагаю это. Сначала создайте ключ, который соответствует одинаковым объектам (A, B), а затем сгруппируйте по динамикам и объектам (ключи).

df['key'] = (df['speaker'] != df['speaker'].shift(1)).astype(int).cumsum()

который дает

  speaker      message  key
0       A  random text    1
1       B  random text    2
2       A  random text    3
3       A  random text    3
4       A  random text    3
5       B  random text    4
6       A  random text    5
7       B  random text    6
8       B  random text    6
9       A  random text    7

Теперь вам просто нужно сгруппировать

df = df.groupby(['key', 'speaker'])['message'].apply(' '.join)
df

который дает

key  speaker
1    A                                  random text
2    B                                  random text
3    A          random text random text random text
4    B                                  random text
5    A                                  random text
6    B                      random text random text
7    A                                  random text

Спасибо за вклад. К сожалению, это не объединяет столбцы сообщений. Я, вероятно, должен был сделать это более ясным в исходном посте!

cookie1986 23.12.2020 13:44

Ах! Теперь это имеет смысл. Я отредактировал свой ответ.

Serge de Gosson de Varennes 23.12.2020 15:01

Спасибо, это здорово, и намного короче, чем мой подход!

cookie1986 23.12.2020 16:00

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

# compare each row with the previous
mydata['prev_speaker'] = mydata['speaker'].shift(1).mask(pd.isnull, mydata['speaker'])

# boolean value to determine whether current speaker differs from previous
mydata['speaker_change'] = np.where(mydata['speaker'] != mydata['prev_speaker'], 'True','False')

# empty list to record changes in speaker
counterList = []    

# initialize a counter to loop through dataframe
counter =1

# loop through dataframe, increasing counter by 1 if the speaker changes
for row in mydata['speaker_change']:
    if row == 'False':
        counterList.append(counter)
    else:
        counter+=1
        counterList.append(counter)

# add counterList to dataframe
mydata['chunking'] = counterList

# group the original message based on the chunking variable
mydata['message'] = mydata.groupby(['chunking'])['message'].transform(lambda x: ' '.join(x))

# drop duplicate rows based on message content and chunking
mydata = mydata.drop_duplicates(subset=['message','chunking'])

# drop non-needed columns
mydata = mydata.drop(['prev_speaker','speaker_change','chunking'], axis=1)

Что теперь дает мне следующее:

|---------------------|-------------------------------------|
|       Speaker       |               Message               |
|---------------------|-------------------------------------|
|          A          |             random text             |
|---------------------|-------------------------------------|
|          B          |             random text             |
|---------------------|-------------------------------------|
|          A          | random text random text random text |
|---------------------|-------------------------------------|
|          B          |             random text             |
|---------------------|-------------------------------------|
|          A          |             random text             |
|---------------------|-------------------------------------|
|          B          |       random text random text       |
|---------------------|-------------------------------------|
|          A          |             random text             |
|---------------------|-------------------------------------|

Другие вопросы по теме