Разбить несколько значений на новые строки - PullRequest
0 голосов
/ 02 мая 2018

У меня есть фрейм данных, в котором несколько столбцов могут иметь несколько значений в одном наблюдении. Каждое наблюдение в этих строках имеет "/" в конце наблюдения, независимо от того, есть ли несколько. Это означает, что некоторые значения выглядят так: «OneThing /», а другие как: «OneThing / AnotherThing /»

Мне нужно взять значения, в которых есть более одного значения в наблюдении, и разбить их на отдельные строки.

Это общий пример того, как выглядит фрейм данных:

ID  Date   Name ColA   ColB   Col_of_Int                        ColC   ColD
1   09/12  Ann  String String OneThing/                         String String
2   09/13  Pete String String OneThing/AnotherThing             String String
3   09/13  Ann  String String OneThing/AnotherThing/ThirdThing/ String String
4   09/12  Pete String String OneThing/                         String String

Что я хочу, чтобы вывод был:

ID  Date   Name ColA   ColB   Col_of_Int                        ColC   ColD
1   09/12  Ann  String String OneThing                         String String
2   09/13  Pete String String OneThing                         String String
2   09/13  Pete String String Another Thing                    String String
3   09/13  Ann  String String OneThing                         String String
3   09/13  Ann  String String AnotherThing                     String String
3   09/13  Ann  String String ThirdThing                       String String
4   09/12  Pete String String OneThing/                        String String

Я пробовал следующее:

df = df[df['Column1'].str.contains('/')]
df_split = df[df['Column1'].str.contains('/')]
df1 = df_split.copy()
df2 = df_split.copy()

split_cols = ['Column1']

for c in split_cols:
    df1[c] = df1[c].apply(lambda x: x.split('/')[0])
    df2[c] = df2[c].apply(lambda x: x.split('/')[1])

new_rows = df1.append(df2)
df.drop(df_split.index, inplace=True)
df = df.append(new_rows, ignore_index=True)

Это работает, но я думаю, что это создает новые строки после каждого '/', что означает, что one новая строка создается для каждого наблюдения только с одним значением (где Я хочу ноль новых строк), и для каждого наблюдения создаются две новые строки с двумя значениями (нужно только одно) и т. Д.

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

Есть ли способ исправить это, чтобы в новые строки добавлялись только наблюдения с более чем одним?

1 Ответ

0 голосов
/ 02 мая 2018

Ваш метод будет работать (я думаю), если вы будете использовать df['column_of_interest'] = df['column_of_interest'].str.rstrip('/'), так как он избавит от этого раздражающего / в конце ваших наблюдений. Однако этот цикл неэффективен, и способ, которым вы его используете, требует, чтобы вы знали, сколько наблюдений у вас максимально в вашем столбце. Вот еще один способ, который, я думаю, достигает того, что вам нужно:

Возьмите этот пример df:

df = pd.DataFrame({'column_of_interest':['onething/', 
                                         'onething/twothings/', 
                                         'onething/twothings/threethings/'], 
                   'values1': [1,2,3], 
                   'values2': [5,6,7]})

>>> df
                column_of_interest  values1  values2
0                        onething/        1        5
1              onething/twothings/        2        6
2  onething/twothings/threethings/        3        7

Это немного запутанно, потому что вы хотите сохранить данные в столбцах за пределами column_of_interest. Таким образом, вы можете временно найти их и отбросить в сторону, используя:

value_columns = [i for i in df.columns if i != 'column_of_interest']

И поместите их в указатель для следующей манипуляции (которая восстанавливает их в конце):

new_df = (df.set_index(value_columns)
          .column_of_interest.str.rstrip('/')
          .str.split('/')
          .apply(pd.Series)
          .stack()
          .rename('new_column_of_interest')
          .reset_index(value_columns))

А ваш new_df тогда выглядит так:

>>> new_df
   values1  values2 new_column_of_interest
0        1        5               onething
0        2        6               onething
1        2        6              twothings
0        3        7               onething
1        3        7              twothings
2        3        7            threethings

Или, альтернативно, используя merge:

new_df = (df[value_columns].merge(df.column_of_interest
                        .str.rstrip('/')
                        .str.split('/')
                        .apply(pd.Series)
                        .stack()
                        .reset_index(1, drop=True)
                        .to_frame('new_column_of_interest'),
                        left_index=True, right_index=True))

РЕДАКТИРОВАТЬ: На размещенном вами кадре данных это приводит к:

   ID   Date  Name    ColA    ColB    ColC    ColD new_column_of_interest
0   1  09/12   Ann  String  String  String  String               OneThing
0   2  09/13  Pete  String  String  String  String               OneThing
1   2  09/13  Pete  String  String  String  String           AnotherThing
0   3  09/13   Ann  String  String  String  String               OneThing
1   3  09/13   Ann  String  String  String  String           AnotherThing
2   3  09/13   Ann  String  String  String  String             ThirdThing
0   4  09/12  Pete  String  String  String  String               OneThing
...