## lamindb.Feature

| class lamindb.Feature(name: str, dtype: SimpleDtype | SimpleDtypeStr | ULabel | Record | Registry | list[Registry] | FieldAttr, type: Feature | None = None, 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, 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"

 Measurable properties such as columns of a sheet.

 Features index variables across datasets to enable querying by
 dimensions (Query & search ).

 [image]

 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 "SimpleDtypeStr".

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

 * **is_type** -- "bool = False" Whether this feature is a 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", attempts to |
 coerce values to the specified dtype during validation, see
 "coerce". Defaults to "False" unless "is_type" is "True".

| * **cat_filters** -- "dict[str, SQLRecord | bool | str] | None = |
 None" Subset a registry by additional filters to define valid
 categories.

| * **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

 Features with simple data types:

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

 A categorical feature measuring labels managed in the "ULabel"
 registry:

 ln.Feature(name="sample", dtype=ln.ULabel).save()

 Restrict a categorical feature to a specific "ULabel" type, here a
 perturbations registry:

 perturbation_registry = ln.ULabel(name="Perturbations", is_type=True).save()
 ln.Feature(name="perturbation", dtype=perturbation_registry).save()

 Restrict a categorical feature to a "Record" type, here an
 experiments registry:

 experiments_registry = ln.Record(name="Experiments", is_type=True).save()
 ln.Feature(name="experiment", dtype=experiments_registry).save()

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

 ln.Feature(name="cell_type_by_expert", dtype=bt.CellType).save()  # expert annotation
 ln.Feature(name="cell_type_by_model", dtype=bt.CellType).save() # model annotation

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

 Scope a feature with a **feature type** to distinguish the same
 feature name across different contexts:

 abc_feature_type = ln.Feature(name="ABC", is_type=True).save()  # ABC could reference a schema, a project, a team, etc.
 ln.Feature(name="concentration_nM", dtype=float, type=abc_feature_type).save()

 xyz_feature_type = ln.Feature(name="XYZ", is_type=True).save()  # XYZ could reference a schema, a project, a team, etc.
 ln.Feature(name="concentration_nM", dtype=float, type=xyz_feature_type).save()

 # calling .save() again with the same name and type returns the existing feature
 ln.Feature(name="concentration_nM", dtype=float, type=xyz_feature_type).save()

 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 sheets 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()

# 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 records indexed by the features of a sheet 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.

# Data types (dtypes)

 **Simple data types.** In  the table below, the first column shows
 the object that can be passed to the "dtype" argument of
 "Feature()" or "Schema()" and the second the string serialization
 that's used in the database.

| --- | --- | --- |
| dtype | string serialization | pandas |
| =================================== | =================================== | =================================== |
| "int" | ""int"" | "int64 | int32 | int16 | int8 |
| uint | ..." |
| --- | --- | --- |
| "float" | ""float"" | "float64 | float32 | float16 |
| float8 | ..." |
| --- | --- | --- |
| "str" | ""str"" | "object" |
| --- | --- | --- |
| "bool" | ""bool"" | "boolean | bool" |
| --- | --- | --- |
| "datetime" | ""datetime"" | "datetime" |
| --- | --- | --- |
| ""datetime64[ns, UTC]"" | ""datetime64[ns, UTC]"" | "datetime64[ns, UTC]" |
| --- | --- | --- |
| "date" | ""date"" | "object" (pandera requires an |
| ISO-format string, convert with |
| "df["date"] = |
| df["date"].dt.date") |
| --- | --- | --- |
| "dict" | ""dict"" | "object" |
| --- | --- | --- |
| ""num"" | ""num"" | "int | float" ("num" is a |
| convenience type for "int |
| float") |
| --- | --- | --- |
| ""path"" | ""path"" | "str" (pandas does not have a |
| dedicated path type, validated as |
| "str") |
| --- | --- | --- |
| ""url"" | ""url"" | "str" (pandas does not have a |
| dedicated url type, validated as |
| "str") |
| --- | --- | --- |

 **Categorical and relational data types.** For any categorical, you
 can restrict permissible values to the values defined in a
 registry. This establishes a relationship.

| --- | --- |
| dtype | string serialization |
| ==================================================== | ==================================================== |
| "ln.ULabel" | ""cat[ULabel]"" |
| --- | --- |
| "bt.CellType" | ""cat[bionty.CellType]"" |
| --- | --- |
| "bt.Disease" | ""cat[bionty.Disease]"" |
| --- | --- |
| "ln.Artifact" | ""cat[Artifact]"" |
| --- | --- |

 You can restrict permissible values to instances of "ULabel" or
 "Record" types, i.e., to dynamic registries.

| --- | --- |
| dtype | string serialization |
| ==================================================== | ==================================================== |
| "ulabel_type" (a "ULabel" with "is_type=True") | ""cat[ULabel[<uid_of_ulabel_type>]]"" |
| --- | --- |
| "record_type" (a "Record" with "is_type=True") | ""cat[Record[<uid_of_record_type>]]"" |
| --- | --- |

 You can restrict permissible values by filtering the categorical on
 fields of its registry.

| --- | --- | --- |
| dtype | cat_filters | string serialization |
| =================================== | =================================== | =================================== |
| "bt.Disease" | "{"source": source}" | ""cat[bionty.Disease]"" |
| --- | --- | --- |
| "ln.Artifact" | "{"schema": schema}" | ""cat[Artifact]"" |
| --- | --- | --- |

 **List data types.**

| --- | --- |
| dtype | string serialization |
| ==================================================== | ==================================================== |
| "list[bt.CellType]" | ""list[cat[bionty.CellType]]"" |
| --- | --- |
| "list[float]" | ""list[float]"" |
| --- | --- |

 **Union data types.**

 Unions are currently only supported for static registries.

| --- | --- |
| dtype | string serialization |
| ==================================================== | ==================================================== |
| "[bt.Tissue.ontology_id, bt.CellType.ontology_id]" | ""cat[bionty.Tissue.ontology_id | bionty.CellType.o |
| ntology_id]"" |
| --- | --- |

| property dtype_as_str: Literal['num', 'int', 'float', 'str', 'bool', 'datetime', 'datetime64[ns, UTC]', 'date', 'dict', 'path', 'url', 'object'] | str | None |

 The "dtype" as a string.

 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 | DeferredAttribute | None |

 The "dtype" as an object.

 -[ 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.

| coerce: bool | None |

 Whether dtypes should be coerced during validation. 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** ("DataFrame") -- Source DataFrame to extract column
 information from

| * **field** ("DeferredAttribute" | "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** ("DeferredAttribute" | "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()

 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.

 Return type:
 "QuerySet"

 save(*args, **kwargs)

 Save the feature to the instance.

 Return type:
 "Feature"

 with_config(optional=None)

 Pass addtional configurations to the schema.

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