панды: как разделить текст в столбце на несколько строк?
Я работаю с большим csv-файлом, и в предпоследнем столбце есть строка текста, которую я хочу разделить определенным разделителем. Мне было интересно, есть ли простой способ сделать это с помощью панд или питона?
CustNum CustomerName ItemQty Item Seatblocks ItemExt
32363 McCartney, Paul 3 F04 2:218:10:4,6 60
31316 Lennon, John 25 F01 1:13:36:1,12 1:13:37:1,13 300
Я хочу разделить на пробел(' ') а потом двоеточие(':') на Seatblocks столбец, но каждая ячейка приведет к различному количеству столбцов. У меня есть функция, чтобы переставить столбцы так Seatblocks столбец находится в конце листа, но я не уверен, что делать оттуда. Я могу сделать это в Excel со встроенным text-to-columns функция и быстрый макрос, но мой набор данных имеет слишком много записей для обработки excel.
в конечном счете, я хочу взять такой записи Джона Леннона и создать несколько строк с информацией из каждого блока мест на отдельной строке.
3 ответов:
Это разбивает блоки сидений по пространству и дает каждому свой собственный ряд.
In [43]: df Out[43]: CustNum CustomerName ItemQty Item Seatblocks ItemExt 0 32363 McCartney, Paul 3 F04 2:218:10:4,6 60 1 31316 Lennon, John 25 F01 1:13:36:1,12 1:13:37:1,13 300 In [44]: s = df['Seatblocks'].str.split(' ').apply(Series, 1).stack() In [45]: s.index = s.index.droplevel(-1) # to line up with df's index In [46]: s.name = 'Seatblocks' # needs a name to join In [47]: s Out[47]: 0 2:218:10:4,6 1 1:13:36:1,12 1 1:13:37:1,13 Name: Seatblocks, dtype: object In [48]: del df['Seatblocks'] In [49]: df.join(s) Out[49]: CustNum CustomerName ItemQty Item ItemExt Seatblocks 0 32363 McCartney, Paul 3 F04 60 2:218:10:4,6 1 31316 Lennon, John 25 F01 300 1:13:36:1,12 1 31316 Lennon, John 25 F01 300 1:13:37:1,13или, чтобы дать каждую строку, разделенную двоеточием, в своем собственном столбце:
In [50]: df.join(s.apply(lambda x: Series(x.split(':')))) Out[50]: CustNum CustomerName ItemQty Item ItemExt 0 1 2 3 0 32363 McCartney, Paul 3 F04 60 2 218 10 4,6 1 31316 Lennon, John 25 F01 300 1 13 36 1,12 1 31316 Lennon, John 25 F01 300 1 13 37 1,13Это немного некрасиво, но, может быть, кто-то перезвонит с более красивым решением.
в отличие от Дэна, я считаю его ответ довольно элегантным... но, к сожалению, это также очень неэффективно. Итак, поскольку вопрос упомянут "большой csv-файл", позвольте мне предложить попробовать в оболочке решение Дэна:
time python -c "import pandas as pd; df = pd.DataFrame(['a b c']*100000, columns=['col']); print df['col'].apply(lambda x : pd.Series(x.split(' '))).head()"... по сравнению с этой альтернативой:
time python -c "import pandas as pd; from scipy import array, concatenate; df = pd.DataFrame(['a b c']*100000, columns=['col']); print pd.DataFrame(concatenate(df['col'].apply( lambda x : [x.split(' ')]))).head()"... а это:
time python -c "import pandas as pd; df = pd.DataFrame(['a b c']*100000, columns=['col']); print pd.DataFrame(dict(zip(range(3), [df['col'].apply(lambda x : x.split(' ')[i]) for i in range(3)]))).head()"второй просто воздерживается от выделения 100 000 серий, и этого достаточно, чтобы сделать его примерно в 10 раз быстрее. Но третье решение, которое несколько иронично тратит много звонков на str.split () (он вызывается один раз на столбец в строке, поэтому в три раза больше, чем для двух других решений), находится вокруг 40 раз быстрее, чем первый, потому что он даже избегает, например, 100 000 списков. И да, это, конечно, немного некрасиво...
EDIT:ответ предлагает, как использовать "to_list()" и чтобы избежать необходимости в лямбда. Получается что-то как
time python -c "import pandas as pd; df = pd.DataFrame(['a b c']*100000, columns=['col']); print pd.DataFrame(df.col.str.split().tolist()).head()"что еще более эффективно, чем третье решение, и, конечно, гораздо более элегантно.
EDIT: еще проще
time python -c "import pandas as pd; df = pd.DataFrame(['a b c']*100000, columns=['col']); print pd.DataFrame(list(df.col.str.split())).head()"работает, а почти как эффективная.
EDIT:еще проще! И обрабатывает NaNs (но менее эффективно):
time python -c "import pandas as pd; df = pd.DataFrame(['a b c']*100000, columns=['col']); print df.col.str.split(expand=True).head()"
import pandas as pd import numpy as np df = pd.DataFrame({'ItemQty': {0: 3, 1: 25}, 'Seatblocks': {0: '2:218:10:4,6', 1: '1:13:36:1,12 1:13:37:1,13'}, 'ItemExt': {0: 60, 1: 300}, 'CustomerName': {0: 'McCartney, Paul', 1: 'Lennon, John'}, 'CustNum': {0: 32363, 1: 31316}, 'Item': {0: 'F04', 1: 'F01'}}, columns=['CustNum','CustomerName','ItemQty','Item','Seatblocks','ItemExt']) print (df) CustNum CustomerName ItemQty Item Seatblocks ItemExt 0 32363 McCartney, Paul 3 F04 2:218:10:4,6 60 1 31316 Lennon, John 25 F01 1:13:36:1,12 1:13:37:1,13 300еще одно подобное решение с цепочкой-это использование
reset_indexиrename:print (df.drop('Seatblocks', axis=1) .join ( df.Seatblocks .str .split(expand=True) .stack() .reset_index(drop=True, level=1) .rename('Seatblocks') )) CustNum CustomerName ItemQty Item ItemExt Seatblocks 0 32363 McCartney, Paul 3 F04 60 2:218:10:4,6 1 31316 Lennon, John 25 F01 300 1:13:36:1,12 1 31316 Lennon, John 25 F01 300 1:13:37:1,13
если в столбце не
NaNзначения, самое быстрое решение-использоватьlistпонимание сDataFrameконструктора:df = pd.DataFrame(['a b c']*100000, columns=['col']) In [141]: %timeit (pd.DataFrame(dict(zip(range(3), [df['col'].apply(lambda x : x.split(' ')[i]) for i in range(3)])))) 1 loop, best of 3: 211 ms per loop In [142]: %timeit (pd.DataFrame(df.col.str.split().tolist())) 10 loops, best of 3: 87.8 ms per loop In [143]: %timeit (pd.DataFrame(list(df.col.str.split()))) 10 loops, best of 3: 86.1 ms per loop In [144]: %timeit (df.col.str.split(expand=True)) 10 loops, best of 3: 156 ms per loop In [145]: %timeit (pd.DataFrame([ x.split() for x in df['col'].tolist()])) 10 loops, best of 3: 54.1 ms per loopно если столбец содержит
NaNработает толькоstr.splitс параметромexpand=TrueвзаменDataFrame(документация), и это объяснить почему это медленнее:df = pd.DataFrame(['a b c']*10, columns=['col']) df.loc[0] = np.nan print (df.head()) col 0 NaN 1 a b c 2 a b c 3 a b c 4 a b c print (df.col.str.split(expand=True)) 0 1 2 0 NaN None None 1 a b c 2 a b c 3 a b c 4 a b c 5 a b c 6 a b c 7 a b c 8 a b c 9 a b c
Comments