Примените функцию к каждой строке блока данных pandas для создания двух новых столбцов

У меня есть pandas DataFrame, st содержащий несколько столбцов:

 <class 'pandas.core.frame.DataFrame'> DatetimeIndex: 53732 entries, 1993-01-07 12:23:58 to 2012-12-02 20:06:23 Data columns: Date(dd-mm-yy)_Time(hh-mm-ss) 53732 non-null values Julian_Day 53732 non-null values AOT_1020 53716 non-null values AOT_870 53732 non-null values AOT_675 53188 non-null values AOT_500 51687 non-null values AOT_440 53727 non-null values AOT_380 51864 non-null values AOT_340 52852 non-null values Water(cm) 51687 non-null values %TripletVar_1020 53710 non-null values %TripletVar_870 53726 non-null values %TripletVar_675 53182 non-null values %TripletVar_500 51683 non-null values %TripletVar_440 53721 non-null values %TripletVar_380 51860 non-null values %TripletVar_340 52846 non-null values 440-870Angstrom 53732 non-null values 380-500Angstrom 52253 non-null values 440-675Angstrom 53732 non-null values 500-870Angstrom 53732 non-null values 340-440Angstrom 53277 non-null values Last_Processing_Date(dd/mm/yyyy) 53732 non-null values Solar_Zenith_Angle 53732 non-null values dtypes: datetime64[ns](1), float64(22), object(1) 

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


Использование:

Я написал функцию, которая принимает Series и возвращает кортеж значений, которые я хочу:

 def calculate(s): a = s['path'] + 2*s['row'] # Simple calc for example b = s['path'] * 0.153 return (a, b) 

Попытка применить это к DataFrame дает ошибку:

 st.apply(calculate, axis=1) --------------------------------------------------------------------------- AssertionError Traceback (most recent call last) <ipython-input-248-acb7a44054a7> in <module>() ----> 1 st.apply(calculate, axis=1) C:\Python27\lib\site-packages\pandas\core\frame.pyc in apply(self, func, axis, broadcast, raw, args, **kwds) 4191 return self._apply_raw(f, axis) 4192 else: -> 4193 return self._apply_standard(f, axis) 4194 else: 4195 return self._apply_broadcast(f, axis) C:\Python27\lib\site-packages\pandas\core\frame.pyc in _apply_standard(self, func, axis, ignore_failures) 4274 index = None 4275 -> 4276 result = self._constructor(data=results, index=index) 4277 result.rename(columns=dict(zip(range(len(res_index)), res_index)), 4278 inplace=True) C:\Python27\lib\site-packages\pandas\core\frame.pyc in __init__(self, data, index, columns, dtype, copy) 390 mgr = self._init_mgr(data, index, columns, dtype=dtype, copy=copy) 391 elif isinstance(data, dict): --> 392 mgr = self._init_dict(data, index, columns, dtype=dtype) 393 elif isinstance(data, ma.MaskedArray): 394 mask = ma.getmaskarray(data) C:\Python27\lib\site-packages\pandas\core\frame.pyc in _init_dict(self, data, index, columns, dtype) 521 522 return _arrays_to_mgr(arrays, data_names, index, columns, --> 523 dtype=dtype) 524 525 def _init_ndarray(self, values, index, columns, dtype=None, C:\Python27\lib\site-packages\pandas\core\frame.pyc in _arrays_to_mgr(arrays, arr_names, index, columns, dtype) 5411 5412 # consolidate for now -> 5413 mgr = BlockManager(blocks, axes) 5414 return mgr.consolidate() 5415 C:\Python27\lib\site-packages\pandas\core\internals.pyc in __init__(self, blocks, axes, do_integrity_check) 802 803 if do_integrity_check: --> 804 self._verify_integrity() 805 806 self._consolidate_check() C:\Python27\lib\site-packages\pandas\core\internals.pyc in _verify_integrity(self) 892 "items") 893 if block.values.shape[1:] != mgr_shape[1:]: --> 894 raise AssertionError('Block shape incompatible with manager') 895 tot_items = sum(len(x.items) for x in self.blocks) 896 if len(self.items) != tot_items: AssertionError: Block shape incompatible with manager 

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


Использование цикла:

Сначала я создал два новых столбца фрейма данных и установил их в None :

 st['a'] = None st['b'] = None 

Затем зациклился на всех индексах и попытался изменить эти значения None которые я получил там, но изменения, которые я сделал, похоже, не работали. То есть, ошибка не была сгенерирована, но DataFrame, похоже, не был изменен.

 for i in st.index: # do calc here st.ix[i]['a'] = a st.ix[i]['b'] = b 

Я думал, что оба эти метода будут работать, но ни один из них не сделал. Итак, что я здесь делаю неправильно? И какой лучший, самый «пуфонический» и «пандаонический» способ сделать это?

Чтобы сделать первый подход, попробуйте вернуть серию вместо кортежа (применить бросает исключение, потому что он не знает, как склеить строки назад вместе, так как количество столбцов не совпадает с исходным фреймом).

 def calculate(s): a = s['path'] + 2*s['row'] # Simple calc for example b = s['path'] * 0.153 return pd.Series(dict(col1=a, col2=b)) 

Второй подход должен работать, если вы замените:

 st.ix[i]['a'] = a 

с:

 st.ix[i, 'a'] = a 

Я всегда использую lambdas и встроенную функцию map() для создания новых строк путем объединения других строк:

 st['a'] = map(lambda path, row: path + 2 * row, st['path'], st['row']) 

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

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

 title_dict = {'male': 'mr.', 'female': 'ms.'} table['title'] = map(lambda title, gender: title if title != None else title_dict[gender], table['title'], table['gender']) 

Это было решено здесь: применить функцию pandas к столбцу для создания нескольких новых столбцов?

Применительно к вашему вопросу это должно работать:

 def calculate(s): a = s['path'] + 2*s['row'] # Simple calc for example b = s['path'] * 0.153 return pd.Series({'col1': a, 'col2': b}) df = df.merge(df.apply(calculate, axis=1), left_index=True, right_index=True) 

Еще одно решение, основанное на назначении новых столбцов в цепочках методов :

 st.assign(a = st['path'] + 2*st['row'], b = st['path'] * 0.153) 

Помните, что assign всегда возвращает копию данных, оставляя исходный DataFrame нетронутым.