Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pandas/core/sample.py: 16%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2Module containing utilities for NDFrame.sample() and .GroupBy.sample()
3"""
5from __future__ import annotations
7from typing import TYPE_CHECKING
9import numpy as np
11from pandas._libs import lib
13from pandas.core.dtypes.generic import (
14 ABCDataFrame,
15 ABCSeries,
16)
18if TYPE_CHECKING:
19 from pandas._typing import (
20 AxisInt,
21 ListLike,
22 )
24 from pandas.core.generic import NDFrame
27def preprocess_weights(
28 obj: NDFrame, weights: str | ListLike, axis: AxisInt
29) -> np.ndarray:
30 """
31 Process and validate the `weights` argument to `NDFrame.sample` and
32 `.GroupBy.sample`.
34 Returns `weights` as an ndarray[np.float64], validated except for normalizing
35 weights (because that must be done groupwise in groupby sampling).
36 """
37 # If a series, align with frame
38 if isinstance(weights, ABCSeries):
39 weights = weights.reindex(obj.axes[axis])
41 # Strings acceptable if a dataframe and axis = 0
42 if isinstance(weights, str):
43 if isinstance(obj, ABCDataFrame):
44 if axis == 0:
45 try:
46 weights = obj[weights]
47 except KeyError as err:
48 raise KeyError(
49 "String passed to weights not a valid column"
50 ) from err
51 else:
52 raise ValueError(
53 "Strings can only be passed to "
54 "weights when sampling from rows on "
55 "a DataFrame"
56 )
57 else:
58 raise ValueError(
59 "Strings cannot be passed as weights when sampling from a Series."
60 )
62 if isinstance(obj, ABCSeries):
63 func = obj._constructor
64 else:
65 func = obj._constructor_sliced
67 weights_arr = np.asarray(func(weights, dtype="float64")._values, dtype=np.float64)
69 if len(weights_arr) != obj.shape[axis]:
70 raise ValueError("Weights and axis to be sampled must be of same length")
72 if lib.has_infs(weights_arr):
73 raise ValueError("weights vector may not include `inf` values")
75 if (weights_arr < 0).any():
76 raise ValueError("weights vector may not include negative values")
78 missing = np.isnan(weights_arr)
79 if missing.any():
80 # Don't modify weights in place
81 weights_arr = weights_arr.copy()
82 weights_arr[missing] = 0
83 return weights_arr
86def process_sampling_size(
87 n: int | None, frac: float | None, replace: bool
88) -> int | None:
89 """
90 Process and validate the `n` and `frac` arguments to `NDFrame.sample` and
91 `.GroupBy.sample`.
93 Returns None if `frac` should be used (variable sampling sizes), otherwise returns
94 the constant sampling size.
95 """
96 # If no frac or n, default to n=1.
97 if n is None and frac is None:
98 n = 1
99 elif n is not None and frac is not None:
100 raise ValueError("Please enter a value for `frac` OR `n`, not both")
101 elif n is not None:
102 if n < 0:
103 raise ValueError(
104 "A negative number of rows requested. Please provide `n` >= 0."
105 )
106 if n % 1 != 0:
107 raise ValueError("Only integers accepted as `n` values")
108 else:
109 assert frac is not None # for mypy
110 if frac > 1 and not replace:
111 raise ValueError(
112 "Replace has to be set to `True` when "
113 "upsampling the population `frac` > 1."
114 )
115 if frac < 0:
116 raise ValueError(
117 "A negative number of rows requested. Please provide `frac` >= 0."
118 )
120 return n
123def sample(
124 obj_len: int,
125 size: int,
126 replace: bool,
127 weights: np.ndarray | None,
128 random_state: np.random.RandomState | np.random.Generator,
129) -> np.ndarray:
130 """
131 Randomly sample `size` indices in `np.arange(obj_len)`.
133 Parameters
134 ----------
135 obj_len : int
136 The length of the indices being considered
137 size : int
138 The number of values to choose
139 replace : bool
140 Allow or disallow sampling of the same row more than once.
141 weights : np.ndarray[np.float64] or None
142 If None, equal probability weighting, otherwise weights according
143 to the vector normalized
144 random_state: np.random.RandomState or np.random.Generator
145 State used for the random sampling
147 Returns
148 -------
149 np.ndarray[np.intp]
150 """
151 if weights is not None:
152 weight_sum = weights.sum()
153 if weight_sum != 0:
154 weights = weights / weight_sum
155 else:
156 raise ValueError("Invalid weights: weights sum to zero")
158 assert weights is not None # for mypy
159 if not replace and size * weights.max() > 1:
160 raise ValueError(
161 "Weighted sampling cannot be achieved with replace=False. Either "
162 "set replace=True or use smaller weights. See the docstring of "
163 "sample for details."
164 )
166 return random_state.choice(obj_len, size=size, replace=replace, p=weights).astype(
167 np.intp, copy=False
168 )