BUG: idxmin & idxmax axis = 1 str reducer for transform
State: closed

- [x] Addresses `idxmin` for #45986 
- [x] [Tests added and passed](https://pandas.pydata.org/pandas-docs/dev/development/contributing_codebase.html#writing-tests) if fixing a bug or adding a new feature.
- [x] All [code checks passed](https://pandas.pydata.org/pandas-docs/dev/development/contributing_codebase.html#pre-commit).
- [x] Added [type annotations](https://pandas.pydata.org/pandas-docs/dev/development/contributing_codebase.html#type-hints) to new arguments/methods/functions.
- [x] Added an entry in the latest `doc/source/whatsnew/vX.X.X.rst` file if fixing a bug or adding a new feature.

To Do:
- [x] Fix failing `test_api_consistency` due to changed signatures.
- [x] Fix `_shared_docs` for `idxmin` to reflect the changes in the signature.

**Description**
- `.transform("idxmin")` & `.transform("idxmax")` now works with `axis=1` groupby objects.
- The default `axis=0` argument is ignored. Instead we directly use the grouper's axis.
- Note that this behavior is different from a generic `idxmin` call which still respects the `axis` argument.

Labels:
name: Bug
name: Groupby
name: Apply
description: Apply, Aggregate, Transform, Map

Comments:
I have implemented the required changes in the latest commit.

We ignore the `axis` parameter, we take it directly based on the `self.axis` and have mentioned the same in the docs.
> Looks good, small requests below. Also, would you be willing to do idxmax here as well?

Sure, I will do that.


@iofall - I think this looks good, just need to resolve a whatsnew conflict
I looked at the failing test, we might need to remove / rewrite it, since it depends on supplying `axis = 1` to `idxmax` which we are ignoring.

https://github.com/pandas-dev/pandas/blob/a28cadbeb6f21da6c768b84473b3415e6efb3115/pandas/tests/groupby/test_function.py#L509-L511

Here, we groupby on line 509, this happens with `axis = 0`, so we do get some groups. But if we change the same line and put `axis = 1`, we don't get any groups.

```python
>>> gb = df.groupby("A")
>>> gb.groups
{1: [0, 3, 6], 2: [1, 4, 7], 3: [2, 5, 8], 4: [9]}

>>> gb = df.groupby("A", axis = 1)
>>> gb.groups
{}
```
Thus the `res` variable comes out as an empty DataFrame, failing the test.

I am not sure how to proceed but we can try:
- The same thing we had done before, honor the `axis` argument if given otherwise use `self.axis`
- Change the test to check for a different result that does not rely on groups across different axes.


I was under the impression `groupby(..., axis=0).idxmin(axis=1)` would raise, but it looks like this is not the case.

> The same thing we had done before, honor the `axis` argument if given otherwise use `self.axis`

+1 on this
> I was under the impression `groupby(..., axis=0).idxmin(axis=1)` would raise, but it looks like this is not the case.

Sorry I did not convey this properly, it does raise but only when the number of rows and columns mismatch in the issue's minimal example.

```python
# raises
df = pd.DataFrame({"a": [1, 2,], "b": [3, 4], "c": [5, 6]}, index=["x", "y"])
result = df.groupby([0, 1, 1], axis=0).transform("idxmin", axis = 1)

# passes
df = pd.DataFrame({"a": [1, 2, 3], "b": [3, 4, 5], "c": [5, 6, 7]}, index=["x", "y", "z"])
result = df.groupby([0, 1, 1], axis=0).transform("idxmin", axis = 1)
```

> +1 on this

What do you recommend? 
- Put `axis` in `kwargs`, that way we can check if it exists in the `kwargs` and handle accordingly.
- Remove the default `axis = 0` and make it a required argument.
- Or make `axis = None` as the default. If `axis` is None, we use `self.axis`, else use whatever value supplied by the user.


@iofall - The code

```
df = pd.DataFrame({"a": [1, 2,], "b": [3, 4], "c": [5, 6]}, index=["x", "y"])
result = df.groupby([0, 1, 1], axis=0)
```

raises because you have two rows to group and you're specifying groups for three rows.

> Or make `axis = None` as the default. If `axis` is None, we use `self.axis`, else use whatever value supplied by the user.

This sounds like a great way forward.
> raises because you have two rows to group and you're specifying groups for three rows.

Sorry I meant even this raises -
```python
df = pd.DataFrame({"a": [1, 2,], "b": [3, 4], "c": [5, 6]}, index=["x", "y"])
result = df.groupby([0, 1, 1], axis=1).transform("idxmin", axis=0)
```
I think we have the right changes now, we have completed what the issue had asked for:

This example now works and all the tests are passing as well.
``` python
df = DataFrame({"a": [1, 2], "b": [3, 4], "c": [5, 6]}, index=["x", "y"])
result = df.groupby([0, 0, 1], axis=1).transform("first")
```
---------------------------------------
However, examples like these still raise
```python
df = pd.DataFrame({"a": [1, 2,], "b": [3, 4], "c": [5, 6]}, index=["x", "y"])
result = df.groupby([0, 1, 1], axis=1).transform("idxmin", axis=0)
```
Although this should be included in another issue or PR, but here is some investigation

https://github.com/pandas-dev/pandas/blob/209de2803c5d772a4b297ac696aa85f67582448a/pandas/core/groupby/groupby.py#L1070-L1076 Specifically for `idxmin` and `idxmax`, when the indexes mismatch, we end up at this function, which tries to reset the `axis` for each of the `v`s in the `values` that are obtained after grouping. However `Series` objects don't have `axis` which results in the error we see in the above `idxmin` call.

If we put a conditional such that `axis` is not checked when `v` is a `Series`,  we do get some output shown below:
```python
     a    b    c
a    x  NaN  NaN
b  NaN    x    x
c  NaN    x    x
```
This might help solve this issue.
Thanks for the update, will take a look in the next day or so. Just  wanted to give my thoughts here:

> Although this should be included in another issue or PR, but here is some investigation

I think the axis argument should be deprecated entirely; see #50405.
Thanks @iofall - very nice!
> Thanks @iofall - very nice!

Thanks! It was fun 🎉