pandas - данные стека из строки с разделителями-запятыми в строки [duplicate]

Когда вы говорите несколько запросов, вы имеете в виду несколько операторов SQL, например:

UPDATE table1 SET a=b WHERE c;
UPDATE table2 SET a=b WHERE d;
UPDATE table3 SET a=b WHERE e;

Или несколько вызовов функций запроса, как в:

mySqlQuery(UPDATE table1 SET a=b WHERE c;)
mySqlQuery(UPDATE table2 SET a=b WHERE d;)
mySqlQuery(UPDATE table3 SET a=b WHERE e;)

сделанный с помощью одного вызова mySqlQuery, если это то, чего вы хотели достичь, просто вызовите функцию mySqlQuery следующим образом:

mySqlQuery(UPDATE table1 SET a=b WHERE c; UPDATE table2 SET a=b WHERE d; UPDATE table3 SET a=b WHERE e;)

Это выполнит все три запроса одним вызовом mySqlQuery ().

90
задан MaxU 10 September 2017 в 17:21
поделиться

13 ответов

Как насчет чего-то вроде этого:

In [55]: pd.concat([Series(row['var2'], row['var1'].split(','))              
                    for _, row in a.iterrows()]).reset_index()
Out[55]: 
  index  0
0     a  1
1     b  1
2     c  1
3     d  2
4     e  2
5     f  2

Тогда вам просто нужно переименовать столбцы

40
ответ дан Chang She 17 August 2018 в 10:26
поделиться
  • 1
    Похоже, это сработает. Спасибо за вашу помощь! В общем, однако, существует ли предпочтительный подход к Split-Apply-Combine, где Apply возвращает размер кадра данных произвольного размера (но согласованный для всех фрагментов), а Combine - просто vstacks возвращаемые DF? – Vincent 2 October 2012 в 01:22
  • 2
    GroupBy.apply должен работать (я просто попробовал его против мастера). Однако в этом случае вам не нужно проходить дополнительный этап группировки, так как вы произвольно генерируете данные по строке? – Chang She 2 October 2012 в 02:43
  • 3
    Да все верно. Спасибо за совет. iterrows приятно. – Vincent 2 October 2012 в 04:00
  • 4
    Эй, ребята. Извините, что вскочил на это так поздно, но задавался вопросом, нет ли лучшего решения для этого. Я пытаюсь экспериментировать с iterrows впервые, так как это похоже на билет для этого. Я также смущен предложенным решением. Что означает "_ & quot; представлять? Можете ли вы объяснить, как работает решение? --Спасибо – horatio1701d 25 June 2014 в 21:20
  • 5
    Можно ли расширить решение до более чем двух столбцов? – horatio1701d 25 June 2014 в 22:54

Другое решение, использующее пакет копирования python

import copy
new_observations = list()
def pandas_explode(df, column_to_explode):
    new_observations = list()
    for row in df.to_dict(orient='records'):
        explode_values = row[column_to_explode]
        del row[column_to_explode]
        if type(explode_values) is list or type(explode_values) is tuple:
            for explode_value in explode_values:
                new_observation = copy.deepcopy(row)
                new_observation[column_to_explode] = explode_value
                new_observations.append(new_observation) 
        else:
            new_observation = copy.deepcopy(row)
            new_observation[column_to_explode] = explode_values
            new_observations.append(new_observation) 
    return_df = pd.DataFrame(new_observations)
    return return_df

df = pandas_explode(df, column_name)
0
ответ дан Ankit Maheshwari 17 August 2018 в 10:26
поделиться

Функция разделения строк может принимать опцию логического аргумента «expand».

Вот решение, использующее этот аргумент:

a.var1.str.split(",",expand=True).set_index(a.var2).stack().reset_index(level=1, drop=True).reset_index().rename(columns={0:"var1"})
1
ответ дан cgels 17 August 2018 в 10:26
поделиться

Похожие вопросы как: pandas: Как разделить текст в столбце на несколько строк?

Вы могли бы сделать:

>> a=pd.DataFrame({"var1":"a,b,c d,e,f".split(),"var2":[1,2]})
>> s = a.var1.str.split(",").apply(pd.Series, 1).stack()
>> s.index = s.index.droplevel(-1)
>> del a['var1']
>> a.join(s)
   var2 var1
0     1    a
0     1    b
0     1    c
1     2    d
1     2    e
1     2    f
25
ответ дан Community 17 August 2018 в 10:26
поделиться
  • 1
    Действительно эффективный & amp; большая помощь для моей проблемы! – bold 12 December 2016 в 01:09
  • 2
    Он работает после добавления еще одного кода переименования s.name = 'var1' – Jesse 4 June 2017 в 07:13
  • 3
    Это работало лучше для меня! – Harry M 24 July 2018 в 19:43

Основываясь на превосходном решении [DMG], [6], здесь представлена ​​универсальная векторная (без петель) функция, которая разбивает столбец кадра данных на несколько строк и объединяет его с исходным фреймворком данных.

def change_column_order(df, col_name, index):
    cols = df.columns.tolist()
    cols.remove(col_name)
    cols.insert(index, col_name)
    return df[cols]

def split_df(dataframe, col_name, sep):
    orig_col_index = dataframe.columns.tolist().index(col_name)
    orig_index_name = dataframe.index.name
    orig_columns = dataframe.columns
    dataframe = dataframe.reset_index()  # we need a natural 0-based index for proper merge
    index_col_name = (set(dataframe.columns) - set(orig_columns)).pop()
    df_split = pd.DataFrame(
        pd.DataFrame(dataframe[col_name].str.split(sep).tolist())
        .stack().reset_index(level=1, drop=1), columns=[col_name])
    df = dataframe.drop(col_name, axis=1)
    df = pd.merge(df, df_split, left_index=True, right_index=True, how='inner')
    df = df.set_index(index_col_name)
    df.index.name = orig_index_name
    # merge adds the column to the last place, so we need to move it back
    return change_column_order(df, col_name, orig_col_index)

Пример:

df = pd.DataFrame([['a:b', 1, 4], ['c:d', 2, 5], ['e:f:g:h', 3, 6]], 
                  columns=['Name', 'A', 'B'], index=[10, 12, 13])
df
        Name    A   B
    10   a:b     1   4
    12   c:d     2   5
    13   e:f:g:h 3   6

split_df(df, 'Name', ':')
    Name    A   B
10   a       1   4
10   b       1   4
12   c       2   5
12   d       2   5
13   e       3   6
13   f       3   6    
13   g       3   6    
13   h       3   6    

Обратите внимание, что он сохраняет исходный индекс и порядок столбцов. Он также работает с dataframes, которые имеют непоследовательный индекс.

2
ответ дан Dennis Golomazov 17 August 2018 в 10:26
поделиться

Просто использовал превосходный ответ jiln сверху, но ему нужно было расширить, чтобы разделить несколько столбцов. Думаю, я бы поделился.

def splitDataFrameList(df,target_column,separator):
''' df = dataframe to split,
target_column = the column containing the values to split
separator = the symbol used to perform the split

returns: a dataframe with each entry for the target column separated, with each element moved into a new row. 
The values in the other columns are duplicated across the newly divided rows.
'''
def splitListToRows(row, row_accumulator, target_columns, separator):
    split_rows = []
    for target_column in target_columns:
        split_rows.append(row[target_column].split(separator))
    # Seperate for multiple columns
    for i in range(len(split_rows[0])):
        new_row = row.to_dict()
        for j in range(len(split_rows)):
            new_row[target_columns[j]] = split_rows[j][i]
        row_accumulator.append(new_row)
new_rows = []
df.apply(splitListToRows,axis=1,args = (new_rows,target_column,separator))
new_df = pd.DataFrame(new_rows)
return new_df
1
ответ дан Derryn Webster-Knife 17 August 2018 в 10:26
поделиться

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

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

b = DataFrame(a.var1.str.split(',').tolist(), index=a.var2).stack()
b = b.reset_index()[[0, 'var2']] # var1 variable is currently labeled 0
b.columns = ['var1', 'var2'] # renaming var1
67
ответ дан DMulligan 17 August 2018 в 10:26
поделиться
  • 1
    Это решение работало значительно быстрее и, по-видимому, использует меньше памяти, – cyril 15 April 2017 в 00:06
  • 2
    Это хорошее векторизованное решение панд, я искал это. Благодаря! – Dennis Golomazov 5 January 2018 в 20:20
  • 3
    – user5359531 23 August 2018 в 22:10

Я придумал решение для dataframes с произвольным количеством столбцов (в то же время только разделяя записи по одному столбцу за раз).

def splitDataFrameList(df,target_column,separator):
    ''' df = dataframe to split,
    target_column = the column containing the values to split
    separator = the symbol used to perform the split

    returns: a dataframe with each entry for the target column separated, with each element moved into a new row. 
    The values in the other columns are duplicated across the newly divided rows.
    '''
    def splitListToRows(row,row_accumulator,target_column,separator):
        split_row = row[target_column].split(separator)
        for s in split_row:
            new_row = row.to_dict()
            new_row[target_column] = s
            row_accumulator.append(new_row)
    new_rows = []
    df.apply(splitListToRows,axis=1,args = (new_rows,target_column,separator))
    new_df = pandas.DataFrame(new_rows)
    return new_df
5
ответ дан jlln 17 August 2018 в 10:26
поделиться
  • 1
    приятно, но грустно медленно из-за этого todict () conversion :( – KWubbufetowicz 22 June 2016 в 18:51

UPDATE2: более общая векторная функция, которая будет работать для нескольких normal и нескольких столбцов list

def explode(df, lst_cols, fill_value=''):
    # make sure `lst_cols` is a list
    if lst_cols and not isinstance(lst_cols, list):
        lst_cols = [lst_cols]
    # all columns except `lst_cols`
    idx_cols = df.columns.difference(lst_cols)

    # calculate lengths of lists
    lens = df[lst_cols[0]].str.len()

    if (lens > 0).all():
        # ALL lists in cells aren't empty
        return pd.DataFrame({
            col:np.repeat(df[col].values, lens)
            for col in idx_cols
        }).assign(**{col:np.concatenate(df[col].values) for col in lst_cols}) \
          .loc[:, df.columns]
    else:
        # at least one list in cells is empty
        return pd.DataFrame({
            col:np.repeat(df[col].values, lens)
            for col in idx_cols
        }).assign(**{col:np.concatenate(df[col].values) for col in lst_cols}) \
          .append(df.loc[lens==0, idx_cols]).fillna(fill_value) \
          .loc[:, df.columns]

Демо:

Несколько list столбцов - все list столбцы должны иметь одинаковые элементы из каждой строки:

In [36]: df
Out[36]:
   aaa  myid        num          text
0   10     1  [1, 2, 3]  [aa, bb, cc]
1   11     2     [1, 2]      [cc, dd]
2   12     3         []            []
3   13     4         []            []

In [37]: explode(df, ['num','text'], fill_value='')
Out[37]:
   aaa  myid num text
0   10     1   1   aa
1   10     1   2   bb
2   10     1   3   cc
3   11     2   1   cc
4   11     2   2   dd
2   12     3
3   13     4

Настройка:

df = pd.DataFrame({
 'aaa': {0: 10, 1: 11, 2: 12, 3: 13},
 'myid': {0: 1, 1: 2, 2: 3, 3: 4},
 'num': {0: [1, 2, 3], 1: [1, 2], 2: [], 3: []},
 'text': {0: ['aa', 'bb', 'cc'], 1: ['cc', 'dd'], 2: [], 3: []}
})

Столбец CSV:

In [46]: df
Out[46]:
        var1  var2 var3
0      a,b,c     1   XX
1  d,e,f,x,y     2   ZZ

In [47]: explode(df.assign(var1=df.var1.str.split(',')), 'var1')
Out[47]:
  var1  var2 var3
0    a     1   XX
1    b     1   XX
2    c     1   XX
3    d     2   ZZ
4    e     2   ZZ
5    f     2   ZZ
6    x     2   ZZ
7    y     2   ZZ

используя этот небольшой трюк, мы можем преобразовать CSV-подобный столбец в столбец list:

In [48]: df.assign(var1=df.var1.str.split(','))
Out[48]:
              var1  var2 var3
0        [a, b, c]     1   XX
1  [d, e, f, x, y]     2   ZZ

UPDATE: общий векторный подход (будет работать и для нескольких столбцов):

Original DF:

In [177]: df
Out[177]:
        var1  var2 var3
0      a,b,c     1   XX
1  d,e,f,x,y     2   ZZ

Решение:

сначала давайте преобразуем строки CSV в списки:

In [178]: lst_col = 'var1' 

In [179]: x = df.assign(**{lst_col:df[lst_col].str.split(',')})

In [180]: x
Out[180]:
              var1  var2 var3
0        [a, b, c]     1   XX
1  [d, e, f, x, y]     2   ZZ

Теперь мы можем это сделать:

In [181]: pd.DataFrame({
     ...:     col:np.repeat(x[col].values, x[lst_col].str.len())
     ...:     for col in x.columns.difference([lst_col])
     ...: }).assign(**{lst_col:np.concatenate(x[lst_col].values)})[x.columns.tolist()]
     ...:
Out[181]:
  var1  var2 var3
0    a     1   XX
1    b     1   XX
2    c     1   XX
3    d     2   ZZ
4    e     2   ZZ
5    f     2   ZZ
6    x     2   ZZ
7    y     2   ZZ

OLD answer:

Вдохновленный решением @AFinkelstein , я хотел сделать его более обобщенным, что может быть применено к DF с более чем два столбца и так же быстро, но почти так же быстро, как и решение Апинкельштейна):

In [2]: df = pd.DataFrame(
   ...:    [{'var1': 'a,b,c', 'var2': 1, 'var3': 'XX'},
   ...:     {'var1': 'd,e,f,x,y', 'var2': 2, 'var3': 'ZZ'}]
   ...: )

In [3]: df
Out[3]:
        var1  var2 var3
0      a,b,c     1   XX
1  d,e,f,x,y     2   ZZ

In [4]: (df.set_index(df.columns.drop('var1',1).tolist())
   ...:    .var1.str.split(',', expand=True)
   ...:    .stack()
   ...:    .reset_index()
   ...:    .rename(columns={0:'var1'})
   ...:    .loc[:, df.columns]
   ...: )
Out[4]:
  var1  var2 var3
0    a     1   XX
1    b     1   XX
2    c     1   XX
3    d     2   ZZ
4    e     2   ZZ
5    f     2   ZZ
6    x     2   ZZ
7    y     2   ZZ
63
ответ дан MaxU 17 August 2018 в 10:26
поделиться
  • 1
    Это должен быть принятый ответ, поскольку он является общим. Спасибо @MaxU! – Robin Trietsch 28 March 2017 в 13:33
  • 2
    @RobinTrietsch, спасибо! :) – MaxU 28 March 2017 в 13:39
  • 3
    чувак, если вы можете открыть дискуссию в Git pandas, я думаю, нам нужна встроенная функция вроде этого !!! Я видел так много вопросов о unlistify и unsesting в SO для панд – Wen 1 September 2017 в 16:45
  • 4
    – Jaskaran Singh Puri 21 August 2018 в 13:52
  • 5

Я придумал следующее решение этой проблемы:

def iter_var1(d):
    for _, row in d.iterrows():
        for v in row["var1"].split(","):
            yield (v, row["var2"])

new_a = DataFrame.from_records([i for i in iter_var1(a)],
        columns=["var1", "var2"])
0
ответ дан Pavel 17 August 2018 в 10:26
поделиться

TL; DR

import pandas as pd
import numpy as np

def explode_str(df, col, sep):
    s = df[col]
    i = np.arange(len(s)).repeat(s.str.count(sep) + 1)
    return df.iloc[i].assign(**{col: sep.join(s).split(sep)})

def explode_list(df, col):
    s = df[col]
    i = np.arange(len(s)).repeat(s.str.len())
    return df.iloc[i].assign(**{col: np.concatenate(s)})

Демонстрация

explode_str(a, 'var1', ',')

  var1  var2
0    a     1
0    b     1
0    c     1
1    d     2
1    e     2
1    f     2

Давайте создадим новый dataframe d, который имеет списки

d = a.assign(var1=lambda d: d.var1.str.split(','))

explode_list(d, 'var1')

  var1  var2
0    a     1
0    b     1
0    c     1
1    d     2
1    e     2
1    f     2

Общие комментарии

Я буду использовать np.arange с repeat для создания позиций индекса данных, которые я могу использовать с iloc.

FAQ

Почему я не использую loc?

Поскольку индекс может быть не уникальным, и использование loc вернет каждую строку, которая соответствует запрошенному индексу.

Почему не используете ли вы values атрибут и срез?

При вызове values, если весь фрагмент данных находится в одном сплоченном «блоке», Pandas вернет представление массива это «блок». В противном случае Pandas придется собирать новый массив. При мольберге этот массив должен иметь одинаковый тип. Часто это означает возврат массива с dtype, который является object. Используя iloc вместо резки атрибута values, я избавляюсь от необходимости иметь дело с этим.

Почему вы используете assign?

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

Почему повторяются значения индекса?

В силу используя iloc на повторных позициях, полученный индекс показывает тот же повторяющийся шаблон. Один повтор для каждого элемента списка или строки. Это можно сбросить с помощью reset_index(drop=True)


Для строк

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

Затем я использую это sep join строки split.

def explode_str(df, col, sep):
    s = df[col]
    i = np.arange(len(s)).repeat(s.str.count(sep) + 1)
    return df.iloc[i].assign(**{col: sep.join(s).split(sep)})

Для списков

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

Я использую Numpy's concatenate, чтобы замять списки вместе.

import pandas as pd
import numpy as np

def explode_list(df, col):
    s = df[col]
    i = np.arange(len(s)).repeat(s.str.len())
    return df.iloc[i].assign(**{col: np.concatenate(s)})

2
ответ дан piRSquared 17 August 2018 в 10:26
поделиться

Вот довольно простое сообщение, которое использует метод split из accessand pandas str, а затем использует NumPy для выравнивания каждой строки в один массив.

Соответствующие значения извлекаются путем повторения нерасширенный столбец правильное количество раз с np.repeat.

var1 = df.var1.str.split(',', expand=True).values.ravel()
var2 = np.repeat(df.var2.values, len(var1) / len(df))

pd.DataFrame({'var1': var1,
              'var2': var2})

  var1  var2
0    a     1
1    b     1
2    c     1
3    d     2
4    e     2
5    f     2
4
ответ дан Ted Petrou 17 August 2018 в 10:26
поделиться
  • 1
    Это может быть очень красивый ответ. К сожалению, он не масштабируется для большого количества столбцов, не так ли? – Michael Dorner 21 June 2018 в 14:08
25
ответ дан Community 6 September 2018 в 07:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: