ENH: 54714 repeated sampling improvement
State: closed

This PR contains enhanced functionality for df.sample() and df.groupby().sample(), with the addition of n_samples. 

Currently, there is no way to take repeated samples to create a sampling distribution using pandas alone. Currently, one would need to create a list comprehension to perform the same, and numpy.random.choice has been preferred for the same as it takes ~700ms compared to the pandas sample method taking ~25s for 1M values. 

The new proposed method takes ~ 25ms for the same and performs the task 1000x faster than the current sample feature.

<img width="982" alt="Screenshot 2023-08-27 at 1 40 11 PM" src="https://github.com/pandas-dev/pandas/assets/25490130/206d07e7-0f9d-4e6f-864d-34ea96e5d780">

- [x] closes #54714
- [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.


Labels:

Comments:
@joelostblom 
Is there a way to improve performance without adding a new keyword?
@mroeschke The goal here is to improve performance when the user wants to run multiple samples on their data. Running a for loop with pd.sample() for n different samples seems to take a lot of time. What the change does is it asks the user for number of samples, gets a flat sample, and reshapes it accordingly. This way we save massive time. 

I think the new keyword is needed, else it would not work as majority of the execution time is taken by pd.sample() function
pandas has a fairly high bar for adding new keywords to APIs since it's already large and would need more buy in from the core team before moving forward here, so investigating performance based on https://github.com/pandas-dev/pandas/issues/54714#issuecomment-1690406400 would be acceptable first
Agreed @mroeschke - I would like to understand why we can't make the current method more performant before entertaining adding a keyword for this use case.
Thanks for starting this @advaithsrao and thanks for the comments @rhshadrach and @mroeschke.

While the performance of `sample` could be improved in multiple ways, one of the main advantages with adding a new parameter is the clarity of the syntax. I teach introductory statistics and data science courses using pandas, and it is quite complicated for a novice of programming to understand what is going on in the current syntax for repeated sampling:

```py
pd.concat([
    df.sample(sample_size).assign(replicate=rep)
    for rep in range(replicates)
])
```

versus the suggested improvement:

```py
df.sample(sample_size, n_samples=replicates)
```

This gives us less versatility in how we teach with pandas since we need to introduce several programing concepts (concatenation, list comprehension/looping, and column assignment) for students to be able to understand how to take a repeated sample. Considering that we want to use pandas in statistics and data science courses that target students who are programming novices, the current behavior is a limitation to us. We teach some of these courses in R currently, where there is a dedicated parameter for repeated sampling:

```
rep_sample_n(df, size = sample_size, reps = replicates)
``` 

In our experience, having a similar syntax in Python would be helpful for both instructors and students.
> This gives us less versatility in how we teach with pandas since we need to introduce several programing concepts (concatenation, list comprehension/looping, and column assignment) for students to be able to understand how to take a repeated sample. 

I appreciate that it does put additional burden on the novice programmer. However when making decisions on expanding the API and the maintenance burden that comes along with it, I don't think we can sustainably add features for the purpose of allowing users to avoid learning common concepts. And I do believe these are common enough.
Thanks for the PR so far, but I think this feature needs more buy-in from other core devs before moving forward here so closing for now.