## lamindb.Feature

| class lamindb.Feature(name: str, dtype: SimpleDtype | SimpleDtypeStr | ULabel | Record | Registry | list[Registry] | FieldAttr, type: Feature | None | Unset = UNSET, is_type: bool = False, unit: str | None = None, description: str | None = None, synonyms: str | None = None, nullable: bool | None = None, default_value: Any | None = None, coerce: bool | None = None, cat_filters: dict[str, SQLRecord | bool | str] | None = None, values_through: Feature | SQLRecordFieldName | None = None, branch: Branch | None = None, space: Space | None = None) |
| class lamindb.Feature(*, name: str, is_type: Literal[True], description: str | None = None) |
class lamindb.Feature(*db_args)

 Bases: "SQLRecord", "HasType", "CanCurate", "HasSynonyms",
 "TracksRun", "TracksUpdates"

 Dataset dimensions such as the columns of a "DataFrame".

 Features validate and index variables across datasets so that you
 can query & load validated datasets → Query, search & load data

 [image]

 By defining features, you define which variables you expect to be
 measured in your database, and validate your datasets against those
 definitions.

 Parameters:
 * **name** -- "str" Name of the feature, typically a column
 name.

| * **dtype** -- "SimpleDtype | ULabel | Record | Registry |
| list[Registry] | FieldAttr" Types or "ULabel" or "Record" |
 objects representing types. See "dtypes" for more details.

| * **type** -- "Feature | None = None" A feature type, see |
 "type".

 * **is_type** -- "bool = False" Whether this is a feature type,
 see "is_type".

| * **unit** -- "str | None = None" Unit of measure, ideally SI |
 (""m"", ""s"", ""kg"", etc.) or ""normalized"" etc.

| * **description** -- "str | None = None" A description. |

| * **synonyms** -- "str | None = None" Bar-separated synonyms. |

 * **nullable** -- "bool = True" Whether the feature can have
 null-like values ("None", "pd.NA", "NaN", etc.), see
 "nullable".

| * **default_value** -- "Any | None = None" Default value for the |
 feature.

| * **coerce** -- "bool | None = None" When "True", coerces this |
 feature's values during validation. "Schema.coerce=True" also
 coerces it when this is left unset. See "coerce".

| * **cat_filters** -- "dict[str, SQLRecord | bool | str] | None = |
 None" For a categorical dtype, filter its related registry
 with these filters.

| * **values_through** -- "Feature | SQLRecordFieldName | None = |
 None" Source of values for this feature. Pass a related
 "Feature" to read and write values through the backward
 relationship; writes store inverted links on the source
 feature. Pass a "SQLRecordFieldName" to store values in a
 "Record" field instead of "RecordJson".

| * **branch** -- "Branch | None = None" A branch. If "None", uses |
 the current branch.

| * **space** -- "Space | None = None" A space. If "None", uses |
 the current space.

 See also:

 "Schema"
 Schemas of datasets such as column sets of dataframes.

 "features"
 The features of an artifact.

# Examples

 For bigger databases, always pass a **feature type** to scope a
 feature to a specific context. For example, you might create a
 feature type for your experiments and another for your samples:

 experiments = ln.Feature(name="Experiments", is_type=True).save()
 samples = ln.Feature(name="Samples", is_type=True).save()

 Features with simple data types:

 ln.Feature(name="sample_note", dtype=str, type=samples).save()
 ln.Feature(name="temperature_in_celsius", dtype=float, type=samples).save()
 ln.Feature(name="read_count", dtype=int, type=experiments).save()

 Restrict a categorical feature to a specific "Record" or "ULabel"
 type:

 samples = ln.Record.get(name="Samples", is_type=True)
 ln.Feature(name="samples", dtype=list[samples], type=experiments).save()

 Restrict a categorical feature to the "bt.CellType" registry:

 ln.Feature(name="cell_type_by_expert", dtype=bt.CellType).save()
 ln.Feature(name="cell_type_by_model", dtype=bt.CellType).save()

 Categoricals define relationships with registries.: For example,
 when passing "ULabel" to "dtype" in "Feature()", one relates the
 new feature to the "ULabel" registry.

 Annotate an artifact with features (works identically for records
 and runs):

 artifact.features.set_values({
 "temperature_in_celsius": 37.5,
 "sample_note": "Control sample",
 })

 Query artifacts/records/runs by features:

 ln.Artifact.filter(features__name="temperature_in_celsius")  # artifacts with this feature
 ln.Artifact.filter(temperature_in_celsius__gt=37) # artifacts where temperature > 37

 Disambiguate duplicate feature names by querying with a "Feature"
 object:

 feature = ln.Feature.get(name="my_ambig_name", type__name="my_feature_type")
 ln.Artifact.filter(feature == "hello")  # instead of my_ambig_name="hello"

 A list "dtype":

 ln.Feature(
 name="cell_types",
 dtype=list[bt.CellType],  # or list[str] for a list of strings
 ).save()

 A path "dtype":

 ln.Feature(
 name="image_path",
 dtype="path", # will be validated as `str`
 ).save()

 Restrict categories via filters:

 # restrict diseases to those matching a specific ontology version
 source = bt.Source.get(name="My ontology")  # a registry for ontology versions
 ln.Feature(
 name="disease",
 dtype=bt.Disease,
 cat_filters={"source": source},
 ).save()

 # restrict artifacts to those matching a specific schema
 schema = ln.Schema.get(name="my-schema")
 ln.Feature(
 name="valid_artifact",
 dtype=ln.Artifact,
 cat_filters={"schema": schema},
 ).save()

 # restrict records to record frames with a shared schema and type
 sample_type = ln.Record.get(name="Samples")
 schema = ln.Schema.get(name="my_sample_schema")
 ln.Feature(
 name="samplesheet",
 dtype=sample_type,
 cat_filters={"is_type": True, "schema": schema},
 ).save()

 A feature accepting multiple categorical types - a union type:

 ln.Feature(
 name="cell_types",
 dtype=[bt.Tissue.ontology_id, bt.CellType.ontology_id]
 ).save()

 A feature that draws its values from a related feature:

 author = ln.Feature(name="author", dtype=ln.Record.get(name="Authors")).save()
 books = ln.Feature(
 name="books",
 dtype=list[ln.Record.get(name="Books")],
 values_through=author,
 ).save()
 # book.author  <-- the author of the book
 # author.books <-- the books written by the author (reverse relation of Book.author)

 A feature that stores values on a "Record" field:

 ln.Feature(name="created_at", dtype=datetime, values_through="created_at").save()
 ln.Feature(name="external_id", dtype=str, values_through="reference").save()

# General notes

 Features can define validation constraints for individual dataset
 dimensions. Here is an example where two flow cytometry datasets
 measure cell markers like "CD4" and "CD8A" and metadata like
 "sample" and "cell_type":

 [image]

 For more, read Curate datasets  or Query arrays in storage .

 -[ Features work across artifacts, records, and runs. ]-

 Here is how data records indexed by the features of a record frame
 look like on the hub UI:

 [image]

 -[ What if my dataset has 40k or more dimensions as in a gene
 expression dataset? ]-

 You don't bother defining an individual feature for each dimension
 but instead define a common "dtype" for a set of features along
 with a constraint for the feature identifier type.

 For example:

 ln.Schema(itype=ln.Feature, dtype=float).save()  # use Feature.name as feature identifier type
 ln.Schema(itype=bt.Gene.ensembl_gene_id, dtype=int).save()  # use Gene.ensembl_gene_id as feature identifier type
 ln.Schema(itype=bt.Protein.uniprot_id, dtype=float).save()  # use Protein.uniprot_id as feature identifier type
 ln.Schema(itype=bt.CellMarker, dtype=float).save()  # use CellMarker.name as feature identifier type

 In these examples, "bionty" registries are used to leverage
 biological entities as feature identifiers. If you pass a dataset
 for validation with this schema, feature identifiers will be
 validated accordingly.

 -[ What is the difference between features and labels? ]-

 1. A feature qualifies what is measured, i.e., a numerical or
 categorical random variable

 2. A label *is* a measured value of a categorical variable, i.e., a
 category

 Example: When annotating a dataset that measures expression of 30k
 genes, the gene identifiers serve as feature identifiers, and the
 features are expression measurements for these genes. When
 annotating a dataset whose experiment knocked out 3 specific genes,
 those genes serve as labels of the dataset.

 Re-shaping data can introduce ambiguity among features & labels. If
 this happened, ask yourself what the joint measurement was: a
 feature qualifies variables in a joint measurement. The canonical
 data matrix lists jointly measured variables in the columns.

# Nullability and optionality

 Nullability controls whether a value may be null, while optionality
 controls whether a feature may be omitted.

 A required nullable feature must be supplied when creating a
 "Record" whose type schema requires it, even if its value is null:

 ln.Record(type=required_type, features={nullable_required_feature: None})

 If the type schema contains only optional features, all features
 may be omitted:

 ln.Record(type=optional_type)

 Features are nullable by default. Set "nullable" in the feature
 constructor to control whether null values are allowed. Features
 passed to the "Schema" constructor are required by default. Pass a
 feature with "with_config()" and "optional=True" to make it
 optional in that schema, or set "minimal_set" to "False" to make
 all listed features optional.

 -[ List and dictionary dtypes ]-

 For list and dictionary features, null values are represented as
 "[]" and "{}". Setting "nullable=False" therefore requires a non-
 empty list or dictionary. Explicit "None" and "NaN" inputs are also
 treated as null. For saved records with a type schema,
 "get_values()" returns required nullable features even when no
 annotation is stored. Null values are returned as "[]" for lists,
 "{}" for dictionaries, and "None" for other types.

| property values_through: Feature | SQLRecordFieldName | None |

 Resolve the object that provides values for this feature.

 Returns:
 The source "Feature" for a reverse record relation, a
 "SQLRecordFieldName" if values are stored on a "SQLRecord"
 field, else "None".

| property related_feature: Feature | None |

 Return the paired feature in the values_through relationship.

 For a derived feature ("books.values_through = author") this
 returns the source ("author"). For a source feature ("author")
 this returns the first derived feature that references it via
 "_aux["vf"]".

 Unsaved features cannot be reverse-resolved from the database,
 so an unsaved source feature returns "None" unless it already
 has an in-memory "values_through" assignment.

| property dtype_as_str: SimpleDtypeStr | str | None |

 The "dtype" as a string.

 See "dtypes" for more details.

 You can query by this property as if it was a string field. The
 query is delegated to the private "_dtype_str" field.

 Is "None" if "Feature" if "is_type=True", otherwise a string.

 -[ Examples ]-

 Query by "dtype_as_str":

 ln.Feature.filter(dtype_as_str="float").to_dataframe()

 Examples for "dtype_as_str":

 feature_float = ln.Feature(name="measurement", dtype=float).save()
 assert feature_float.dtype_as_str == "float"

 sample_type = bt.Record(name="Sample", is_type=True).save()
 feature_sample = ln.Feature(name="sample", dtype=sample_type).save()
 assert feature_sample.dtype_as_str == "cat[Record[12345678abcdeFGHI]]  # uid of type record

 feature_list_float = ln.Feature(name="numbers", dtype=list[float]).save()
 assert feature_list_float.dtype_as_str == "list[float]"

 feature_ulabel = ln.Feature(name="sample", dtype=ln.ULabel).save()
 assert feature_ulabel.dtype_as_str == "cat[ULabel]"

 feature_record = ln.Feature(name="sample", dtype=bt.CellLine).save()
 assert feature_record.dtype_as_str == "cat[bionty.CellLine]"

 feature_list_record = ln.Feature(name="cell_types", dtype=list[bt.CellLine]).save()
 assert feature_list_record.dtype_as_str == "list[cat[bionty.CellLine]]"

| property dtype_as_object: type | SQLRecord | FieldAttr | None |

 The "dtype" as an object.

 See "dtypes" for more details.

 -[ Example ]-

 For simple dtypes, returns the built-in Python type:

 feature_float = ln.Feature(name="measurement", dtype=float).save()
 assert feature_float.dtype_as_object is float

 For features with with "Record" or "ULabel" types, returns the
 "Record" or "ULabel" object:

 sample_type = bt.Record(name="Sample", is_type=True).save()
 feature_sample = ln.Feature(name="sample", dtype=sample_type).save()
 assert feature_sample.dtype_as_object == sample_type

 For features with "Registry" types, returns the "Registry"
 object or a field ("DeferredAttribute") object:

 feature_cell_type = ln.Feature(name="cell_type_name", dtype=bt.CellType).save()
 assert feature_cell_type.dtype_as_object == bt.CellType
 feature_ontology_id = ln.Feature(name="ontology_id", dtype=bt.CellType.ontology_id).save()
 assert feature_ontology_id.dtype_as_object == bt.CellType.ontology_id

 uid: str

 Universal id, valid across DB instances.

 name: str

 Name of feature.

| unit: str | None |

 Unit of measure, ideally SI ("m", "s", "kg", etc.) or
 'normalized' etc. (optional).

| description: str | None |

 A description.

 array_rank: int

 Rank of feature.

 Number of indices of the array: 0 for scalar, 1 for vector, 2
 for matrix.

 Is called ".ndim" in "numpy" and "pytorch" but shouldn't be
 confused with the dimension of the feature space.

 array_size: int

 Number of elements of the feature.

 Total number of elements (product of shape components) of the
 array.

 * A number or string (a scalar): 1 or "None"

 * A 50-dimensional embedding: 50

 * A 25 x 25 image: 625

| array_shape: list[int] | None |

 Shape of the feature.

 * A number or string (a scalar): [1] or "None"

 * A 50-dimensional embedding: [50]

 * A 25 x 25 image: [25, 25]

 Is stored as a list rather than a tuple because it's serialized
 as JSON.

| synonyms: str | None |

| Bar-separated ( | ) synonyms (optional). |

| default_value: Any | None |

 A default value that overwrites missing values during
 standardization.

| nullable: bool | None |

 Whether the feature can have nullable values. "None" for type-
 like features.

 For list and dictionary features, null values are represented as
 an empty list ("[]") and an empty dictionary ("{}"),
 respectively.

| coerce: bool | None |

 Whether this feature's values are coerced to its dtype during
 validation.

 "True" coerces this feature even when the schema leaves "coerce"
 unset. See "coerce" for how the two flags combine.

 "None" for type-like features.

| type: Feature | None |

 Type of feature (e.g., 'Readout', 'Metric', 'Metadata',
 'ExpertAnnotation', 'ModelPrediction').

 Allows to group features by type, e.g., all read outs, all
 metrics, etc.

 schemas: RelatedManager[Schema]

 Schemas linked to this feature.

 features: RelatedManager[Feature]

 Features of this type (can only be non-empty if "is_type" is
 "True").

 values: RelatedManager[JsonValue]

 Values for this feature.

 projects: RelatedManager[Project]

 Annotating projects.

 ablocks: RelatedManager[FeatureBlock]

 Attached blocks ← "feature".

 classmethod from_dataframe(df, field=None, *, mute=False)

 Create Feature records for dataframe columns.

 Parameters:
 * **df** (pd.DataFrame) -- Source DataFrame to extract column
 information from

| * **field** (FieldAttr | None, default: "None") -- FieldAttr |
 for Feature model validation, defaults to Feature.name

 * **mute** (bool, default: "False") -- Whether to mute
 Feature creation similar names found warnings

 Return type:
 SQLRecordList

 classmethod from_dict(dictionary, field=None, *, type=None, mute=False)

 Create Feature records for dictionary keys.

 Parameters:
 * **dictionary** (dict[str, Any]) -- Source dictionary to
 extract key information from

| * **field** (FieldAttr | None, default: "None") -- FieldAttr |
 for Feature model validation, defaults to "Feature.name"

| * **type** (Feature | None, default: "None") -- Feature type |
 of all created features

 * **mute** (bool, default: "False") -- Whether to mute dtype
 inference and feature creation warnings

 Return type:
 SQLRecordList

 is_null(value=True)

 Build a predicate for null-style feature presence checks.

 Example:

 perturbation = ln.Feature.get(name="perturbation")
 ln.Artifact.filter(perturbation.is_null(False)).to_dataframe(
 include="features"
 )

 Return type:
 "FeaturePredicate"

 query_features(depth=None)

 Query features of sub types.

 While ".features" retrieves the features with the current type,
 this method also retrieves sub types and the features with sub
 types of the current type.

 Parameters:
| **depth** ("int" | "None", default: "None") -- How many type |
 hops to follow. "None" walks the whole subtree. "1" returns
 only the direct features of this type.

 Return type:
 "QuerySet"

 save(*args, **kwargs)

 Save the feature in the database.

 Return type:
 "Feature"

 with_config(optional=None)

 Pass additional configuration to "Schema".

 Parameters:
| **optional** ("bool" | "None", default: "None") -- Whether |
 this feature is optional in a schema.

 Return type:
 "tuple"["Feature", "dict"["str", "bool"]]