Missing Values¶
Missing values occur when no data value is stored for the variable in an observation and are most often represented with a NULL or None. Not handling them can lead to unexpected results (for example, some ML algorithms can’t handle missing values at all) and worse, it can lead to incorrect conclusions.
There are 3 main types of missing values:
MCAR (Missing Completely at Random): The events that lead to any particular data-item being missing occur entirely at random. For example, in IOT, we can lose sensory data in transmission.
MAR (Missing {Conditionally} at Random): Missing data doesn’t happen at random and is instead related to some of the observed data. For example, some students may have not answered to some specific questions of a test because they were absent during the relevant lesson.
MNAR (Missing not at Random): The value of the variable that’s missing is related to the reason it’s missing. For example, if someone didn’t subscribe to a loyalty program, we can leave the cell empty.
Different types of missing values tend to suggest different methods for imputing them. For example, when dealing with MCAR values, you can use mathematical aggregations to impute the missing values. For MNAR values, we can simply create another category. MAR values, however, we’ll need to do some more investigation before deciding how to impute the data.
To see how to handle missing values in VerticaPy, we’ll use the well-known titanic dataset.
from verticapy.datasets import load_titanic
titanic = load_titanic()
titanic.head(100)
123 pclass100% | ... | 123 body9% | Abc 57% | |
| 1 | 1 | ... | [null] | |
| 2 | 1 | ... | [null] | |
| 3 | 1 | ... | [null] | |
| 4 | 1 | ... | [null] | |
| 5 | 1 | ... | [null] | |
| 6 | 1 | ... | 208 | |
| 7 | 1 | ... | [null] | |
| 8 | 1 | ... | [null] | |
| 9 | 1 | ... | [null] | |
| 10 | 1 | ... | [null] | |
| 11 | 1 | ... | [null] | |
| 12 | 1 | ... | [null] | |
| 13 | 1 | ... | 275 | |
| 14 | 1 | ... | 147 | |
| 15 | 1 | ... | 307 | |
| 16 | 1 | ... | [null] | |
| 17 | 1 | ... | [null] | |
| 18 | 1 | ... | 258 | |
| 19 | 1 | ... | [null] | |
| 20 | 1 | ... | [null] | |
| 21 | 1 | ... | 249 | |
| 22 | 1 | ... | [null] | |
| 23 | 1 | ... | [null] | |
| 24 | 1 | ... | [null] | |
| 25 | 1 | ... | [null] | |
| 26 | 1 | ... | 96 | |
| 27 | 1 | ... | [null] | |
| 28 | 1 | ... | 245 | |
| 29 | 1 | ... | [null] | |
| 30 | 1 | ... | [null] | |
| 31 | 1 | ... | [null] | |
| 32 | 1 | ... | [null] | |
| 33 | 1 | ... | [null] | |
| 34 | 1 | ... | [null] | |
| 35 | 1 | ... | [null] | |
| 36 | 1 | ... | [null] | |
| 37 | 1 | ... | [null] | |
| 38 | 1 | ... | [null] | |
| 39 | 1 | ... | [null] | |
| 40 | 1 | ... | [null] | |
| 41 | 1 | ... | [null] | |
| 42 | 1 | ... | [null] | |
| 43 | 1 | ... | [null] | |
| 44 | 1 | ... | [null] | |
| 45 | 1 | ... | [null] | |
| 46 | 1 | ... | [null] | |
| 47 | 1 | ... | [null] | |
| 48 | 1 | ... | [null] | |
| 49 | 1 | ... | [null] | |
| 50 | 1 | ... | [null] | |
| 51 | 1 | ... | [null] | |
| 52 | 1 | ... | [null] | |
| 53 | 1 | ... | [null] | |
| 54 | 1 | ... | [null] | |
| 55 | 1 | ... | [null] | |
| 56 | 1 | ... | [null] | |
| 57 | 1 | ... | [null] | |
| 58 | 1 | ... | [null] | |
| 59 | 1 | ... | [null] | |
| 60 | 1 | ... | [null] | |
| 61 | 1 | ... | [null] | |
| 62 | 1 | ... | [null] | |
| 63 | 1 | ... | [null] | |
| 64 | 1 | ... | [null] | |
| 65 | 1 | ... | [null] | |
| 66 | 1 | ... | [null] | |
| 67 | 1 | ... | [null] | |
| 68 | 1 | ... | [null] | |
| 69 | 1 | ... | [null] | |
| 70 | 1 | ... | [null] | |
| 71 | 1 | ... | [null] | |
| 72 | 1 | ... | [null] | |
| 73 | 1 | ... | [null] | |
| 74 | 1 | ... | [null] | |
| 75 | 1 | ... | [null] | |
| 76 | 1 | ... | [null] | |
| 77 | 1 | ... | [null] | |
| 78 | 1 | ... | [null] | |
| 79 | 1 | ... | [null] | |
| 80 | 1 | ... | [null] | |
| 81 | 1 | ... | [null] | |
| 82 | 1 | ... | [null] | |
| 83 | 1 | ... | [null] | |
| 84 | 1 | ... | [null] | |
| 85 | 1 | ... | [null] | |
| 86 | 2 | ... | [null] | |
| 87 | 2 | ... | [null] | |
| 88 | 2 | ... | [null] | |
| 89 | 2 | ... | [null] | |
| 90 | 2 | ... | [null] | |
| 91 | 2 | ... | [null] | |
| 92 | 2 | ... | [null] | |
| 93 | 2 | ... | [null] | |
| 94 | 2 | ... | [null] | |
| 95 | 2 | ... | 322 | |
| 96 | 2 | ... | [null] | |
| 97 | 2 | ... | [null] | |
| 98 | 2 | ... | [null] | |
| 99 | 2 | ... | [null] | |
| 100 | 2 | ... | 256 |
We can examine the missing values with the count() method.
titanic.count_percent()
| ... | count | percent | |
| "pclass" | ... | 1234.0 | 100.0 |
| "survived" | ... | 1234.0 | 100.0 |
| "name" | ... | 1234.0 | 100.0 |
| "sex" | ... | 1234.0 | 100.0 |
| "sibsp" | ... | 1234.0 | 100.0 |
| "parch" | ... | 1234.0 | 100.0 |
| "ticket" | ... | 1234.0 | 100.0 |
| "fare" | ... | 1233.0 | 99.919 |
| "embarked" | ... | 1232.0 | 99.838 |
| "age" | ... | 997.0 | 80.794 |
| "home.dest" | ... | 706.0 | 57.212 |
| "boat" | ... | 439.0 | 35.575 |
| "cabin" | ... | 286.0 | 23.177 |
| "body" | ... | 118.0 | 9.562 |
The missing values for boat are MNAR; missing values simply indicate that the passengers didn’t pay for a lifeboat. We can replace all the missing values with a new category No Lifeboat using the fillna() method.
titanic["boat"].fillna("No Lifeboat")
titanic["boat"]
Abc boat | |
| 1 | No Lifeboat |
| 2 | No Lifeboat |
| 3 | No Lifeboat |
| 4 | No Lifeboat |
| 5 | No Lifeboat |
| 6 | No Lifeboat |
| 7 | No Lifeboat |
| 8 | No Lifeboat |
| 9 | No Lifeboat |
| 10 | No Lifeboat |
| 11 | No Lifeboat |
| 12 | No Lifeboat |
| 13 | No Lifeboat |
| 14 | No Lifeboat |
| 15 | No Lifeboat |
| 16 | No Lifeboat |
| 17 | 14 |
| 18 | No Lifeboat |
| 19 | No Lifeboat |
| 20 | No Lifeboat |
Missing values for age seem to be MCAR, so the best way to impute them is with mathematical aggregations. Let’s impute the age using the average age of passengers of the same sex and class.
titanic["age"].fillna(
method = "avg",
by = ["pclass", "sex"],
)
titanic["age"]
123 age | |
| 1 | 36.5 |
| 2 | 0.42 |
| 3 | 25.0 |
| 4 | 14.0 |
| 5 | 16.0 |
| 6 | 31.0 |
| 7 | 25.0 |
| 8 | 12.0 |
| 9 | 20.0 |
| 10 | 24.0 |
| 11 | 32.0 |
| 12 | 20.0 |
| 13 | 26.0 |
| 14 | 26.2142058823529 |
| 15 | 32.0 |
| 16 | 26.2142058823529 |
| 17 | 27.0 |
| 18 | 26.2142058823529 |
| 19 | 21.0 |
| 20 | 20.0 |
The features embarked and fare have a couple missing values. Instead of using a technique to impute them, we can just drop them with the dropna() method.
titanic["fare"].dropna()
titanic["embarked"].dropna()
123 pclass100% | ... | 123 survived100% | Abc home.dest57% | |
| 1 | 1 | ... | 0 | Montevideo, Uruguay |
| 2 | 1 | ... | 0 | Trenton, NJ |
| 3 | 1 | ... | 0 | [null] |
| 4 | 1 | ... | 0 | Montevideo, Uruguay |
| 5 | 1 | ... | 0 | Los Angeles, CA |
| 6 | 1 | ... | 0 | Lakewood, NJ |
| 7 | 1 | ... | 0 | Montreal, PQ |
| 8 | 1 | ... | 0 | Deephaven, MN / Cedar Rapids, IA |
| 9 | 1 | ... | 0 | New York, NY |
| 10 | 1 | ... | 0 | Scituate, MA |
| 11 | 1 | ... | 0 | [null] |
| 12 | 1 | ... | 0 | New York, NY |
| 13 | 1 | ... | 0 | [null] |
| 14 | 1 | ... | 0 | London / Middlesex |
| 15 | 1 | ... | 0 | Brighton, MA |
| 16 | 1 | ... | 0 | New York, NY |
| 17 | 1 | ... | 0 | New York, NY |
| 18 | 1 | ... | 0 | Springfield, MA |
| 19 | 1 | ... | 0 | Vancouver, BC |
| 20 | 1 | ... | 0 | Dorchester, MA |
The fillna() method offers many options. Let’s use the help() function to view its parameters.
help(titanic["embarked"].fillna)
Help on function fillna in module verticapy.core.vdataframe._fill:
fillna(val: Union[int, float, str, datetime.datetime, datetime.date] = None, method: Literal['auto', 'mode', '0ifnull', 'mean', 'avg', 'median', 'ffill', 'pad', 'bfill', 'backfill'] = 'auto', expr: Union[str, verticapy.core.string_sql.base.StringSQL] = '', by: Optional[Annotated[Union[str, list[str]], 'STRING representing one column or a list of columns']] = None, order_by: Optional[Annotated[Union[str, list[str]], 'STRING representing one column or a list of columns']] = None) -> 'vDataFrame'
Fills missing elements in the vDataColumn with a user-specified
rule.
Parameters
----------
val: PythonScalar / date, optional
Value used to impute the vDataColumn.
method: dict, optional
Method used to impute the missing values.
- auto:
Mean for the numerical and Mode for the
categorical vDataColumns.
- bfill:
Back Propagation of the next element (Constant
Interpolation).
- ffill:
Propagation of the first element (Constant
Interpolation).
- mean:
Average.
- median:
Median.
- mode:
Mode (most occurent element).
- 0ifnull:
0 when the vDataColumn is null, 1 otherwise.
expr: str, optional
SQL string.
by: SQLColumns, optional
vDataColumns used in the partition.
order_by: SQLColumns, optional
List of the vDataColumns used to sort the data when using
TS methods.
Returns
-------
vDataFrame
self._parent
print(titanic.current_relation())
(
SELECT
*
FROM
(
SELECT
"pclass",
"survived",
"name",
"sex",
COALESCE("age", AVG("age") OVER (PARTITION BY "pclass", "sex")) AS "age",
"sibsp",
"parch",
"ticket",
"fare",
"cabin",
"embarked",
COALESCE("boat", 'No Lifeboat') AS "boat",
"body",
"home.dest"
FROM
"public"."titanic")
VERTICAPY_SUBTABLE
WHERE ("fare" IS NOT NULL) AND ("embarked" IS NOT NULL))
VERTICAPY_SUBTABLE
Depending on the circumstances, we’ll need to investigate to find the most suitable solution.
In conclusion, before imputing missing data, you have to understand why it might be missing and how it relates to the rest of your dataset.