Как запустить функцию для каждой ячейки на основе индекса, имени столбца и самой ячейки?

#python #pandas

#python #pandas

Вопрос:

У меня есть фрейм данных, подобный этому:

 Name    GoogleMinutes FacebookMinutes
Alice   10            2
Bob     15            3
Chuck   40            10
  

Я хочу запустить для каждой ячейки функцию с подписью func(cell_value,name,site_minutes) .

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

Как я могу это сделать? Я попробовал применить и не сработало.

Редактировать:

Нерабочий пример с применением:

 p = PersonDataProvider()
s = SMDataProvider()
df.apply(lambda x: p.get_wealthness(x.index) * s.get_ticket(x.column) * x)
  

РЕДАКТИРОВАТЬ 2: Важное предупреждение заключается в том, что у меня неизвестное количество столбцов, несмотря на то, что они не показаны в примере.

Комментарии:

1. Можете ли вы опубликовать рабочий пример с приложением?

2. Я отредактировал вопрос.

Ответ №1:

Можете ли вы проверить, работает ли это для вас:

     import pandas as pd


    def main():
        df = pd.DataFrame(
            {'Name': ['Alice', 'Bob', 'Chuck', 'pete'], 'GoogleMinutes': [10, 15, 40, 4], 'FacebookMinutes': [2, 3, 10, 7]})
    
        for a in range(df.shape[0]):
            cell_value = a
            name = df['Name'][a]
            site_minutes = (df['GoogleMinutes'][a], df['FacebookMinutes'][a])
            func1(cell_value, name, site_minutes)
    
    
    def func1(cell_value, name, site_minutes):
        print(cell_value, name, site_minutes)
        return None
    
    
    if __name__ == '__main__':
        main()
  

Ответ №2:

Похоже, вам нужно внести две простые модификации в ваш код:

a) укажите ось (1 или строку).

б) измените ‘index’ на ‘name’.

Вот так:

 names = ['Alice', 'Bob', 'Chuck']
vals = [[10,2],[15,3], [40,10]]
col_names = ['GoogleMinutes', 'FacebookMinutes']
tst = pd.DataFrame(vals, index=names, columns=col_names)

tst['result'] = tst.apply(lambda x: ' '.join((x.name, str(x.GoogleMinutes), str(x.FacebookMinutes))) , axis=1)
  

Ответ №3:

Решение

Обычно вы могли бы использовать pandas.DataFrame.applymap для применения вашей функции поэлементно.

 import pandas as pd

# df is your dataframe
# df.applymap(lambda x: func(x))
prepare_dataframe(df, axis=0).applymap(lambda cell: func(*cell))
  

Однако ваш вариант использования немного отличается. Таким образом, вы могли бы использовать функцию custome prepare_dataframe() для предварительной обработки фрейма данных перед использованием .applymap() на нем.

Реализация кода — подробный пример

Следующий фрагмент кода был запущен в Google Colab, и кажется, что использование axis=0 , когда number of rows > number of columns выполняется быстрее.

 %time prepare_dataframe(df, axis=0).applymap(lambda cell: func(*cell))
# CPU times: user 5.02 ms, sys: 0 ns, total: 5.02 ms
# Wall time: 4.77 ms

# Or

%time prepare_dataframe(df, axis=1).applymap(lambda cell: func(*cell))
# CPU times: user 13.5 ms, sys: 1 ms, total: 14.5 ms
# # Wall time: 23 ms
  

Вывод:

   |                x   |                y
--------------------------------------------
0 |  Out: (0, 'x', 0)  |  Out: (0, 'y', -5)
1 |  Out: (1, 'x', 1)  |  Out: (1, 'y', -1)
2 |  Out: (2, 'x', 2)  |  Out: (2, 'y', 5)
3 |  Out: (3, 'x', 3)  |  Out: (3, 'y', 13)
4 |  Out: (4, 'x', 4)  |  Out: (4, 'y', 23)
  

Пользовательские функции

 def prepare_dataframe(df, axis=0):
    # apply along axis = 0 or 1
    # axis = 0 is faster 
    dff = df.copy()
    index = dff.index
    columns = dff.columns
    nrows, ncols = dff.shape
    if axis==1:
        dff = dff.T
        for idx in index:            
            dff[idx] = tuple(zip([idx]*ncols, columns, df.loc[idx].values))
        dff = dff.T
    else:    
        for col in columns:
            dff[col] = tuple(zip(index, [col]*nrows, df[col].values))
    return dff

# This is the dummy cell level function
def func(index=0, column='A', value=0):
    return f'Out: {(index, column, value)}'

func()
# Out: (0, 'A', 0)
  

Примечание

В вашем случае func() будет следующим:

 p = PersonDataProvider()
s = SMDataProvider()
def func(index=0, column='A', value=0):
    return p.get_wealthness(index) * s.get_ticket(column) * value
  

Фиктивные данные

 import numpy as np
import pandas as pd

x = np.arange(5)
y = x**2   3*x - 5
df = pd.DataFrame({'x': x, 'y': y})
print(df)

#    x   y
# 0  0  -5
# 1  1  -1
# 2  2   5
# 3  3  13
# 4  4  23
  

Ссылки

Комментарии:

1. @lispguy: Да, это не работает как есть с .applymap. Ваш вариант использования требует некоторой предварительной обработки. Итак, я обновил решение. Пожалуйста, проверьте сейчас. Это должно сработать у вас.