Quick answer: np.logical_and() combines Boolean inputs element by element and follows NumPy broadcasting rules. Build each comparison as its own mask, combine masks explicitly, and inspect the shape before filtering. Use np.all() or logical_and.reduce() when the question is whether every value along an axis is true; do not use Python’s scalar and with whole arrays.

np.logical_and() combines two conditions element by element. Each output item is True only when both matching input items are true.
The official NumPy logical_and documentation describes the ufunc. The broader NumPy logic routines page lists related Boolean operations, and the ufunc.reduce documentation explains reduction behavior.
Use logical_and() when you want an array-safe version of the Python and idea. It works across arrays, supports broadcasting, and returns a Boolean array that can be used as a mask. logical_and returns an element-wise Boolean array; NumPy any() Boolean Array Guide reduces Boolean results to answer whether any value is true along an axis.
The function is most useful when each row or item must satisfy two requirements at the same time. Common examples include filtering a numeric range, keeping records with two quality checks, or selecting coordinates that fall inside a rectangular area.
Combine Two Boolean Arrays
The simplest use passes two Boolean arrays with matching shapes.
import numpy as np
left = np.array([True, True, False, False])
right = np.array([True, False, True, False])
result = np.logical_and(left, right)
print(result)
The output is true only at positions where both arrays are true. In this example, only the first pair satisfies both conditions.
This element-by-element behavior is the main reason to use NumPy’s logical functions instead of Python’s scalar operators.
The result has the same shape as the inputs, so it can be inspected directly before it is used for filtering. That makes mistakes easier to catch in small examples.
Build A Numeric Mask
Most real examples start with numeric comparisons. Each comparison creates a Boolean array, and logical_and() combines them.
import numpy as np
scores = np.array([42, 67, 81, 95, 58])
in_range = np.logical_and(scores >= 60, scores <= 90)
print(in_range)
print(scores[in_range])
The mask selects scores between 60 and 90 inclusive. Boolean masks are useful because they filter the original array without writing an explicit loop.
Keep each condition in parentheses or named separately when expressions become long. Clear masks are easier to debug than dense one-line formulas.
For data cleaning, this style is usually better than deleting items step by step. Build the mask once, print or test it, and then apply it to the source array.

Use Broadcasting With logical_and
logical_and() follows NumPy broadcasting rules. A scalar condition can be combined with an array condition.
import numpy as np
values = np.array([-3, -1, 0, 2, 4, 7])
positive_even = np.logical_and(values > 0, values % 2 == 0)
print(positive_even)
print(values[positive_even])
The first condition checks positivity, and the second condition checks divisibility by two. The output mask keeps only positive even numbers.
Broadcasting also works with compatible array shapes, such as a column condition and a row condition in a two-dimensional calculation.
When broadcasting is involved, check the shapes if the output is unexpected. A scalar is simple, but two arrays with different dimensions must still be compatible under NumPy’s broadcasting rules.
Combine Conditions Across A Matrix
For matrices, the function still compares matching elements and returns an array with the same shape.
import numpy as np
data = np.array([
[4, 12, 18],
[7, 15, 21],
])
mask = np.logical_and(data >= 10, data < 20)
print(mask)
print(data[mask])
This keeps values from 10 through 19 anywhere in the matrix. The final filtered result is one-dimensional because Boolean indexing extracts the matching items.
If row and column structure matters after filtering, keep the mask and use it alongside the original matrix instead of flattening the selected values.
This matters in image processing, grid calculations, and tabular numeric work. The mask can preserve where the matches happened even when the selected values alone lose their original position.

Use The & Operator Carefully
For NumPy arrays, the & operator can combine Boolean masks, but each comparison must be wrapped in parentheses.
import numpy as np
ages = np.array([16, 22, 35, 70])
mask = (ages >= 18) & (ages < 65)
print(mask)
print(ages[mask])
The parentheses are required because comparison and bitwise operator precedence can otherwise produce the wrong expression. np.logical_and() can be clearer for readers who are newer to NumPy.
Do not use Python’s and between arrays. It expects a single truth value, not one result per element.
If a condition needs to be reused, store it with a descriptive name. Named masks make chained filtering easier to read and reduce the chance of mixing up comparison boundaries.
Reduce Several Conditions
Because logical_and is a ufunc, it can reduce several Boolean rows into one combined result.
import numpy as np
conditions = np.array([
[True, True, False, True],
[True, False, False, True],
[True, True, True, True],
])
combined = np.logical_and.reduce(conditions, axis=0)
print(combined)
The result is true only in columns where every condition row is true. This is a clean way to combine several masks without chaining many calls.
For readability, name the condition array or build it from named masks before reducing. That makes it easier to understand what each row represents.
Reduction is useful when the number of conditions changes at runtime. Instead of manually chaining calls, collect the masks in an array and reduce them along the condition axis.
Common Mistakes
Do not write condition_a and condition_b for NumPy arrays. Use np.logical_and(condition_a, condition_b) or parenthesized masks with &.
Do not forget that missing values can affect masks. If a mask contains nullable data from another library, convert or clean it before indexing a NumPy array.
Do not assume logical_and() changes the original data. It returns a new Boolean array, which you can store, inspect, or use for filtering.
The practical default is to create clear comparison masks first, combine them with np.logical_and(), and then use the resulting Boolean array to select the values you need.

Build A Range Mask Explicitly
Parenthesize each comparison and combine the resulting arrays with logical_and. This makes the two conditions visible and avoids Python’s scalar truth-value rules, which are not the same as NumPy’s elementwise logic.
import numpy as np
values = np.array([-3, -1, 0, 2, 5])
nonnegative = values >= 0
small = values < 5
mask = np.logical_and(nonnegative, small)
print(mask)
print(values[mask])
Understand Broadcasting Shapes
Two inputs do not need identical shapes when they are broadcast-compatible. A row threshold and a column threshold can combine into a two-dimensional mask, but a shape mismatch should be fixed instead of hidden with a reshape guess.
import numpy as np
values = np.array([[1, 8, 3], [5, 2, 9]])
low = np.array([[0], [3]])
high = np.array([7, 7, 10])
mask = np.logical_and(values >= low, values <= high)
print(mask)
print(mask.shape)

Use where For Conditional Results
A Boolean mask is useful for selecting rows, but sometimes you need a value array with a replacement for false positions. np.where expresses that choice directly and keeps the result shape predictable.
import numpy as np
values = np.array([2, 7, 4, 9])
mask = np.logical_and(values >= 3, values <= 8)
labels = np.where(mask, "accepted", "rejected")
print(labels)
Reduce A Boolean Result
logical_and() returns one result per broadcasted position. Reduce only when the application asks a different question, such as whether all columns satisfy a condition or whether every row passes validation.
import numpy as np
checks = np.array([[True, True, False], [True, True, True]])
print(np.all(checks, axis=1))
print(np.logical_and.reduce(checks, axis=1))
print(np.logical_and.reduce(checks))
Use the NumPy reference for logical_and and the related all reduction. For array comparisons, compare any(), count_nonzero(), and isclose() when deciding how a mask should be summarized.
For related Boolean-array reductions, compare any(), all(), and count_nonzero() after building an elementwise logical mask.
Frequently Asked Questions
What does NumPy logical_and() do?
It applies a logical AND element by element to two broadcast-compatible inputs and returns a Boolean result.
How do I combine two NumPy filter conditions?
Create each Boolean array separately and pass them to np.logical_and(), or use the bitwise & operator with parenthesized array comparisons.
Does logical_and support broadcasting?
Yes. Inputs must be broadcast-compatible, and the result follows the broadcasted shape.
How do I check whether all values are true?
Use np.all() for a clear reduction across an axis, or np.logical_and.reduce() when the ufunc reduction itself is the important operation.