## 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:

 [image]

 Guide: Query, search & load data

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

 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" or "Record"
 type:

 perturbations = ln.Record.get(name="Perturbations", is_type=True)
 ln.Feature(name="perturbation", dtype=perturbations).save()
 experiments = ln.Record.get(name="Experiments", is_type=True)
 ln.Feature(name="experiment", dtype=experiments).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 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()

 Define 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)

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

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

 See "dtypes".

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

 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.

 -[ 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 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"]]