-
-
Notifications
You must be signed in to change notification settings - Fork 18.5k
TYP: Add annotation for df.pivot #32197
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 31 commits
7e461a1
1314059
8bcb313
24c3ede
dea38f2
cd9e7ac
e5e912b
f337468
1cc1225
046cdc9
2807b63
2fdd875
5453237
18e85bb
f61dcac
5e3ac3f
ffda679
6b81abb
c01f221
1552f4b
29b0605
4d2d6d3
f7fb25a
80e9710
7618b3b
d44b3df
3c3c7a4
cb6473d
6586410
76d319a
45637fc
62edda6
f063a24
1ac9e0f
c17dd80
16798b1
63643aa
7b10761
9870d0f
4e05ec0
65b45a0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,8 @@ | ||
from typing import TYPE_CHECKING, Callable, Dict, List, Tuple, Union | ||
from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Sequence, Tuple, Union | ||
|
||
import numpy as np | ||
|
||
from pandas._typing import Label | ||
from pandas.util._decorators import Appender, Substitution | ||
|
||
from pandas.core.dtypes.cast import maybe_downcast_to_dtype | ||
|
@@ -424,42 +425,48 @@ def _convert_by(by): | |
|
||
@Substitution("\ndata : DataFrame") | ||
@Appender(_shared_docs["pivot"], indents=1) | ||
def pivot(data: "DataFrame", index=None, columns=None, values=None) -> "DataFrame": | ||
def pivot( | ||
data: "DataFrame", | ||
index: Optional[Union[Label, Sequence[Optional[Label]]]] = None, | ||
columns: Optional[Union[Label, Sequence[Optional[Label]]]] = None, | ||
values: Optional[Union[Label, Sequence[Optional[Label]]]] = None, | ||
) -> "DataFrame": | ||
if columns is None: | ||
raise TypeError("pivot() missing 1 required argument: 'columns'") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if this is a required kwarg, should the Optional be removed from the signature? cc @simonjayhawkins not sure how to handle this given that the default value is None There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are a few considerations here.
If Optional was omitted, this could add to the confusion. Since columns should allow None, if a column is named None. so the default and check here should probably be _lib.no_default. _lib.no_default currently resolves to Any (defined in cython with no stubs) so is compatible with anything. If we typed _lib.no_default which is currently object() we wouldn't be able to type parameters precisely since object() wouldn't be compatible. In the future we may wish to define using Enums but this would add to the noise of the function signature from an end user perspective. from https://www.python.org/dev/peps/pep-0484/#support-for-singleton-types-in-unions
|
||
columns = columns if is_list_like(columns) else [columns] | ||
|
||
if values is None: | ||
cols: List[str] = [] | ||
cols: List[Optional[Sequence]] = [] | ||
if index is None: | ||
pass | ||
elif is_list_like(index): | ||
cols = list(index) | ||
# Remove type ignore once mypy-5206 is implemented, same for below | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you add a "TODO: " here? makes it easier to track these down There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you add an assert here to make mypy happy? |
||
cols = list(index) # type: ignore | ||
else: | ||
cols = [index] | ||
cols.extend(columns) | ||
cols = [index] # type: ignore | ||
cols.extend(columns) # type: ignore | ||
|
||
append = index is None | ||
indexed = data.set_index(cols, append=append) | ||
else: | ||
if index is None: | ||
index = [Series(data.index, name=data.index.name)] | ||
idx_list = [Series(data.index, name=data.index.name)] | ||
elif is_list_like(index): | ||
index = [data[idx] for idx in index] | ||
idx_list = [data[idx] for idx in index] # type: ignore | ||
else: | ||
index = [data[index]] | ||
idx_list = [data[index]] | ||
|
||
data_columns = [data[col] for col in columns] | ||
index.extend(data_columns) | ||
index = MultiIndex.from_arrays(index) | ||
data_columns = [data[col] for col in columns] # type: ignore | ||
idx_list.extend(data_columns) | ||
mi_index = MultiIndex.from_arrays(idx_list) | ||
|
||
if is_list_like(values) and not isinstance(values, tuple): | ||
# Exclude tuple because it is seen as a single column name | ||
indexed = data._constructor( | ||
data[values]._values, index=index, columns=values | ||
data[values]._values, index=mi_index, columns=values # type: ignore | ||
) | ||
else: | ||
indexed = data._constructor_sliced(data[values]._values, index=index) | ||
indexed = data._constructor_sliced(data[values]._values, index=mi_index) | ||
return indexed.unstack(columns) | ||
|
||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does
Optional[Label]
mean something distinct fromLabel
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what i want to express here is that it could be an empty
[]
, so i put anOptional
here, maybe it's already covered inLabel
? @jbrockmendelThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes,
Label
is defined asOptional[Hashable]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i t hink would be ok to add these to _typing, maybe Labels? so this becomes
Optional[Labels]
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah there's a difference between Optional used for keyword args and Optional added to
Label
to cover None since Hashable doesn't include None.In general, an alias in typing would not include Optional.
so
Optional[Labels]
would be correct andLabels
would be defined accordingly. I thinkUnion[Label, Sequence[Label]]
would be sufficient.empty is different again.
where
Union[builtins.int, None]
==Optional[int]
so
Optional
is different from empty.Optional[<type>]
is syntatic sugar forUnion[<type>, None]
. An empty list is compatible with lists of any type.In mypy, I don't think it is possible to define a type as an empty list (it is possible for a tuple)
from https://www.python.org/dev/peps/pep-0484/#the-typing-module