Filter an R Data Frame with at Least N Non-NA Values per Row

When a data frame contains missing values, you may want to retain only the rows that have a minimum amount of usable data. For example, in a data frame with four columns, you can keep only rows containing at least two, three, or four non-NA values.

In base R, count the missing values in each row with rowSums(is.na(...)), or count the non-missing values directly with rowSums(!is.na(...)). Then use the resulting logical vector to subset the data frame.

Base R Syntax for Keeping Rows with at Least N Non-NA Values

To filter rows of a dataframe that has atleast N non-NAs, use dataframe subsetting as shown below

</>
Copy
resultDF = mydataframe[rowSums(is.na(mydataframe[ , 0:ncol(mydataframe)])) <= (ncol(mydataframe) - N), ]

This expression counts the NA values in each row. A row is retained when its number of missing values is no greater than the total number of columns minus N.

An equivalent and more direct expression is to count non-missing values:

</>
Copy
resultDF <- mydataframe[rowSums(!is.na(mydataframe)) >= N, ]

where

  • mydataframe is the data frame containing complete and missing values.
  • N is the minimum number of non-NA values required in each retained row.
  • resultDF is the filtered data frame.

Example 1 – Filter R Dataframe with minimum N non-NAs

In this example, we will create a Dataframe containing rows with different number of NAs.

</>
Copy
> mydataframe = data.frame(x = c(9, NA, 7, 4), y = c(4, NA, NA, 21), z = c(9, 8, NA, 74), p = c(NA, 63, NA, 2))
> mydataframe
   x  y  z  p
1  9  4  9 NA
2 NA NA  8 63
3  7 NA NA NA
4  4 21 74  2

Now, we will filter this dataframe such that the output contains only rows with atleast 2 non-NAs.

</>
Copy
> N = 2
> resultDF = mydataframe[rowSums(is.na(mydataframe[ , 0:ncol(mydataframe)])) <= (ncol(mydataframe) - N), ]
> resultDF
   x  y  z  p
1  9  4  9 NA
2 NA NA  8 63
4  4 21 74  2
>

Rows 1, 2, and 4 each contain at least two non-missing values. Row 3 contains only one non-NA value, so it is excluded.

Let us try with N = 3.

</>
Copy
> N=3
> resultDF = mydataframe[rowSums(is.na(mydataframe[ , 0:ncol(mydataframe)])) <= (ncol(mydataframe) - N), ]
> resultDF
  x  y  z  p
1 9  4  9 NA
4 4 21 74  2
>

With N = 3, only rows 1 and 4 qualify. Row 1 contains three non-missing values, while row 4 contains four.

Count Non-NA Values in Each R Data Frame Row

Before filtering, you can inspect the number of available values in every row. Applying !is.na() returns TRUE for each non-missing value, and rowSums() counts those values row by row.

</>
Copy
non_na_count <- rowSums(!is.na(mydataframe))
non_na_count
1 2 3 4
3 2 1 4

The result shows that rows 1 through 4 contain three, two, one, and four non-NA values respectively. You can also save these counts in the data frame when they are useful for later analysis.

</>
Copy
mydataframe$non_na_count <- rowSums(!is.na(mydataframe))

Filter Rows Using Only Selected R Data Frame Columns

Sometimes the minimum count should be calculated from only a few relevant columns rather than the complete data frame. Select those columns before applying is.na().

</>
Copy
columns_to_check <- c("x", "y", "z")
N <- 2

resultDF <- mydataframe[
  rowSums(!is.na(mydataframe[columns_to_check])) >= N,
]

This keeps rows that have at least two non-missing values across x, y, and z. Values in column p do not affect the filter.

Filter Rows with at Least N Non-NA Values Using dplyr

In dplyr, use if_any(), if_all(), or rowSums() depending on the condition. To keep rows with at least N non-missing values across all columns, calculate the count inside filter().

</>
Copy
library(dplyr)

N <- 2

resultDF <- mydataframe |>
  filter(rowSums(!is.na(pick(everything()))) >= N)

To check only selected columns, pass those column names to pick().

</>
Copy
resultDF <- mydataframe |>
  filter(rowSums(!is.na(pick(x, y, z))) >= 2)

Filter Rows with No NA Values or At Least One Non-NA Value

The same counting method handles common missing-value filters.

  • To keep fully complete rows, require the number of non-NA values to equal the number of columns.
  • To keep rows containing at least one usable value, require the count to be at least 1.
  • To keep rows with an exact number of non-missing values, compare the count with ==.
</>
Copy
# Keep rows with no NA values
complete_rows <- mydataframe[rowSums(!is.na(mydataframe)) == ncol(mydataframe), ]

# Keep rows with at least one non-NA value
partly_complete_rows <- mydataframe[rowSums(!is.na(mydataframe)) >= 1, ]

# Keep rows with exactly two non-NA values
exactly_two <- mydataframe[rowSums(!is.na(mydataframe)) == 2, ]

For complete rows, complete.cases(mydataframe) is a concise alternative:

</>
Copy
complete_rows <- mydataframe[complete.cases(mydataframe), ]

Why NA Comparisons Should Use is.na() in R

Do not test missing values with expressions such as x == NA or x != NA. An unknown value cannot be compared as an ordinary number or string, so those expressions return NA rather than a useful TRUE or FALSE result.

Use is.na(x) to identify missing values and !is.na(x) to identify non-missing values.

</>
Copy
is.na(x)
!is.na(x)

Common Errors When Filtering R Rows by Non-NA Count

  • Using NA == NA instead of is.na().
  • Counting all columns when only a selected group should determine row completeness.
  • Using na.omit() when partially complete rows should remain in the result.
  • Setting N greater than the number of checked columns, which produces an empty result.
  • Including a previously created count column in a later non-NA calculation and unintentionally changing the threshold logic.

FAQs About Filtering R Rows with Non-NA Values

How do I filter for non-NA values in R?

For one column, use df[!is.na(df$column), ]. With dplyr, use filter(df, !is.na(column)).

How do I keep rows with at least two non-NA values in R?

Use df[rowSums(!is.na(df)) >= 2, ]. The expression counts available values in each row and retains rows whose count is at least two.

How do I filter NA values across multiple selected columns?

Select the columns before counting, as in df[rowSums(!is.na(df[c("a", "b", "c")])) >= N, ].

What is the difference between complete.cases() and a non-NA threshold?

complete.cases() retains only rows with no missing values in the checked columns. A threshold condition such as rowSums(!is.na(df)) >= N can retain partially complete rows.

How do I count the number of non-NA observations in each row?

Use rowSums(!is.na(df)). It returns one count for each row in the data frame.

Editorial QA Checklist for R Non-NA Row Filters

  • Verify that each example distinguishes between counting NA values and counting non-NA values.
  • Confirm that the stated value of N matches the rows shown in each result.
  • Check whether the filter should inspect all columns or only a named subset.
  • Ensure that is.na() is used instead of direct equality comparisons with NA.
  • Confirm that newly added R syntax and output blocks use the correct PrismJS classes.

Summary of Filtering R Data Frames by Non-NA Count

Use rowSums(!is.na(mydataframe)) >= N to keep rows with at least N non-missing values. Select specific columns before counting when only part of the data frame should determine eligibility. For rows that must contain no missing values, use complete.cases(). In this R Tutorial, we have learned to filter a Dataframe based on the number of non-NAs (or ofcourse NAs) in a row.