Я пытаюсь понять, почему списковые методы в кадрах данных ведут себя так же, как списковые методы в Series.
По сути, в Series, если вы используете apply или Transform для списковых методов, они сначала попытаются передать каждое значение Series отдельно в функцию. Если это не сработает, они передадут в функцию всю серию. По той же логике для фрейма данных сначала в функцию должна быть передана строка/столбец или, а затем, если она не работает, должен быть передан весь фрейм данных.
Но это не так, поведение фрейма данных для функций, подобных списку, точно такое же, как и для серий: сначала проверяется отдельные значения, а затем целые столбцы. Уровень детализации не увеличивается. Я хочу понять, почему так написано. Я видел исходный код.
Подробно
Я изучаю команды Apply и Transform в pandas. Я получаю то же самое, когда предоставляется одна функция.
- Для серий
— Функция применяется к каждому значению.Код: Выделить всё
apply - — функция передается команде Apply. Если это не работает, то функции передается вся серия.
Код: Выделить всё
transform
- — Функция применяется к каждому столбцу/строке
Код: Выделить всё
apply - — функция передается команде Apply. Если это не работает, то функции передается весь фрейм данных.
Код: Выделить всё
transform
При использовании аргументов функции в виде списка возникает путаница. Документация для всех вышеперечисленных функций сообщает одно и то же через параметр by_row. В нем говорится следующее:
by_row : False или «compat», по умолчанию «compat»
... Если func представляет собой список или набор вызываемых объектов, сначала попытается преобразовать каждую функцию в методы pandas. Если это не сработает, попытаемся снова вызвать Apply с помощью by_row="compat" и, если это не удастся, снова вызовем Apply с by_row=False (обратная совместимость)...
Теперь давайте начнем с функции Apply. И для Series, и для DataFrames все сводится к этому фрагменту исходного кода для функций, подобных спискам.
Код: Выделить всё
def apply_compat(self):
obj = self.obj
func = self.func
if callable(func):
f = com.get_cython_func(func)
if f and not self.args and not self.kwargs:
return obj.apply(func, by_row=False)
try:
result = obj.apply(func, by_row="compat")
except (ValueError, AttributeError, TypeError):
result = obj.apply(func, by_row=False)
return result
Код: Выделить всё
def func(val):
print("Val - ", val, "\nVal Type - ", type(val), "\n")
if not isinstance(val, pd.Series):
raise TypeError("Not Series")
return val.iloc[0:3]
sr = pd.Series([1,2,3,4,5])
sr.apply([func]) # passed the function as a list
--- Output
Val - 1
Val Type -
Val - 0 1
1 2
2 3
3 4
4 5
dtype: int64
Val Type -
func
0 1
1 2
2 3
Код: Выделить всё
def transform_str_or_callable(self, func) -> DataFrame | Series:
obj = self.obj
args = self.args
kwargs = self.kwargs
if isinstance(func, str):
return self._apply_str(obj, func, *args, **kwargs)
# Two possible ways to use a UDF - apply or call directly
try:
return obj.apply(func, args=args, **kwargs)
except Exception:
return func(obj, *args, **kwargs)
Код: Выделить всё
def func(val):
print("Val - ", val, "\nVal Type - ", type(val), "\n")
if not isinstance(val, pd.Series):
raise Exception("Not Series")
return val
sr = pd.Series([1,2,3,4,5])
sr.transform([func])
--- Output
Val - 1
Val Type -
Val - 0 1
1 2
2 3
3 4
4 5
dtype: int64
Val Type -
func
0 1
1 2
2 3
3 4
4 5
Код: Выделить всё
def func(val):
print("Val - ", val, "\nVal Type - ", type(val), "\n")
if not isinstance(val, pd.DataFrame):
raise TypeError("Not DataFrame")
return val.iloc[0:3]
df = pd.DataFrame([[1,2],[3,4],[5,6]], columns=['A', 'B'])
df.apply([func])
--- Output
Val - 1
Val Type -
Val - 0 1
1 3
2 5
Name: A, dtype: int64
Val Type -
TypeError: Not DataFrame
Код: Выделить всё
def func(val):
print("Val - ", val, "\nVal Type - ", type(val), "\n")
if not isinstance(val, pd.DataFrame):
raise Exception("Not DataFrame")
return val
df = pd.DataFrame([[1,2],[3,4],[5,6]], columns=['A', 'B'])
df.transform([func])
--- Output
Val - 1
Val Type -
Val - 0 1
1 3
2 5
Name: A, dtype: int64
Val Type -
ValueError: Transform function failed
Код: Выделить всё
if is_list_like(func) and not is_dict_like(func):
func = cast(list[AggFuncTypeBase], func)
# Convert func equivalent dict
if is_series:
func = {com.get_callable_name(v) or v: v for v in func}
else:
func = dict.fromkeys(obj, func)
----
for name, how in func.items():
colg = obj._gotitem(name, ndim=1)
results[name] = colg.transform(how, 0, *args, **kwargs)
Я хочу знать, почему он не был разработан как Series. Сначала попробуйте часть объекта, а затем передайте весь объект. Итак, сначала попробуйте использовать строку/столбец фрейма данных, а если это не слова, то передайте весь фрейм данных. Я пытаюсь что-то запомнить, но поведение методов, подобных спискам, полностью отличается от поведения методов, не похожих на списки, в кадрах данных.
Подробнее здесь: https://stackoverflow.com/questions/798 ... -in-pandas
Мобильная версия