|
1 | 1 | # Contains code from https://github.com/pandas-dev/pandas/blob/main/pandas/core/generic.py
|
2 | 2 | from __future__ import annotations
|
3 | 3 |
|
4 |
| -from typing import Iterator, Literal, Optional |
| 4 | +from typing import Callable, Iterator, Literal, Optional, TYPE_CHECKING |
5 | 5 |
|
6 | 6 | from bigframes_vendored.pandas.core import indexing
|
| 7 | +import bigframes_vendored.pandas.core.common as common |
7 | 8 |
|
8 | 9 | from bigframes import constants
|
9 | 10 |
|
| 11 | +if TYPE_CHECKING: |
| 12 | + from bigframes_vendored.pandas.pandas._typing import T |
| 13 | + |
10 | 14 |
|
11 | 15 | class NDFrame(indexing.IndexingMixin):
|
12 | 16 | """
|
@@ -963,6 +967,105 @@ def expanding(self, min_periods=1):
|
963 | 967 | """
|
964 | 968 | raise NotImplementedError(constants.ABSTRACT_METHOD_ERROR_MESSAGE)
|
965 | 969 |
|
| 970 | + def pipe( |
| 971 | + self, |
| 972 | + func: Callable[..., T] | tuple[Callable[..., T], str], |
| 973 | + *args, |
| 974 | + **kwargs, |
| 975 | + ) -> T: |
| 976 | + """ |
| 977 | + Apply chainable functions that expect Series or DataFrames. |
| 978 | +
|
| 979 | + **Examples:** |
| 980 | +
|
| 981 | + Constructing a income DataFrame from a dictionary. |
| 982 | +
|
| 983 | + >>> import bigframes.pandas as bpd |
| 984 | + >>> import numpy as np |
| 985 | + >>> bpd.options.display.progress_bar = None |
| 986 | +
|
| 987 | + >>> data = [[8000, 1000], [9500, np.nan], [5000, 2000]] |
| 988 | + >>> df = bpd.DataFrame(data, columns=['Salary', 'Others']) |
| 989 | + >>> df |
| 990 | + Salary Others |
| 991 | + 0 8000 1000.0 |
| 992 | + 1 9500 <NA> |
| 993 | + 2 5000 2000.0 |
| 994 | + <BLANKLINE> |
| 995 | + [3 rows x 2 columns] |
| 996 | +
|
| 997 | + Functions that perform tax reductions on an income DataFrame. |
| 998 | +
|
| 999 | + >>> def subtract_federal_tax(df): |
| 1000 | + ... return df * 0.9 |
| 1001 | + >>> def subtract_state_tax(df, rate): |
| 1002 | + ... return df * (1 - rate) |
| 1003 | + >>> def subtract_national_insurance(df, rate, rate_increase): |
| 1004 | + ... new_rate = rate + rate_increase |
| 1005 | + ... return df * (1 - new_rate) |
| 1006 | +
|
| 1007 | + Instead of writing |
| 1008 | +
|
| 1009 | + >>> subtract_national_insurance( |
| 1010 | + ... subtract_state_tax(subtract_federal_tax(df), rate=0.12), |
| 1011 | + ... rate=0.05, |
| 1012 | + ... rate_increase=0.02) # doctest: +SKIP |
| 1013 | +
|
| 1014 | + You can write |
| 1015 | +
|
| 1016 | + >>> ( |
| 1017 | + ... df.pipe(subtract_federal_tax) |
| 1018 | + ... .pipe(subtract_state_tax, rate=0.12) |
| 1019 | + ... .pipe(subtract_national_insurance, rate=0.05, rate_increase=0.02) |
| 1020 | + ... ) |
| 1021 | + Salary Others |
| 1022 | + 0 5892.48 736.56 |
| 1023 | + 1 6997.32 <NA> |
| 1024 | + 2 3682.8 1473.12 |
| 1025 | + <BLANKLINE> |
| 1026 | + [3 rows x 2 columns] |
| 1027 | +
|
| 1028 | + If you have a function that takes the data as (say) the second |
| 1029 | + argument, pass a tuple indicating which keyword expects the |
| 1030 | + data. For example, suppose ``national_insurance`` takes its data as ``df`` |
| 1031 | + in the second argument: |
| 1032 | +
|
| 1033 | + >>> def subtract_national_insurance(rate, df, rate_increase): |
| 1034 | + ... new_rate = rate + rate_increase |
| 1035 | + ... return df * (1 - new_rate) |
| 1036 | + >>> ( |
| 1037 | + ... df.pipe(subtract_federal_tax) |
| 1038 | + ... .pipe(subtract_state_tax, rate=0.12) |
| 1039 | + ... .pipe( |
| 1040 | + ... (subtract_national_insurance, 'df'), |
| 1041 | + ... rate=0.05, |
| 1042 | + ... rate_increase=0.02 |
| 1043 | + ... ) |
| 1044 | + ... ) |
| 1045 | + Salary Others |
| 1046 | + 0 5892.48 736.56 |
| 1047 | + 1 6997.32 <NA> |
| 1048 | + 2 3682.8 1473.12 |
| 1049 | + <BLANKLINE> |
| 1050 | + [3 rows x 2 columns] |
| 1051 | +
|
| 1052 | + Args: |
| 1053 | + func (function): |
| 1054 | + Function to apply to this object. |
| 1055 | + ``args``, and ``kwargs`` are passed into ``func``. |
| 1056 | + Alternatively a ``(callable, data_keyword)`` tuple where |
| 1057 | + ``data_keyword`` is a string indicating the keyword of |
| 1058 | + ``callable`` that expects this object. |
| 1059 | + args (iterable, optional): |
| 1060 | + Positional arguments passed into ``func``. |
| 1061 | + kwargs (mapping, optional): |
| 1062 | + A dictionary of keyword arguments passed into ``func``. |
| 1063 | +
|
| 1064 | + Returns: |
| 1065 | + same type as caller |
| 1066 | + """ |
| 1067 | + return common.pipe(self, func, *args, **kwargs) |
| 1068 | + |
966 | 1069 | def __nonzero__(self):
|
967 | 1070 | raise ValueError(
|
968 | 1071 | f"The truth value of a {type(self).__name__} is ambiguous. "
|
|
0 commit comments