16. Manage Feature Life Cycle
Manage Feature Life Cycle¶
In this tutorial, you will learn how to:
- Update Default Feature Job Settings at the table level when there are changes in source table availability or freshness.
- Modify Default Cleaning Operations at the table level when data quality issues are detected.
- Create new version of features that show discrepancies with table defaults.
- Create a new version of a feature list.
- Delete/Deprecate features, feature lists, and their associated guardrails.
Important Note for FeatureByte Enterprise Users with Approval Flow¶
For catalogs with Approval Flow enabled, any changes to table metadata trigger a review process.
This process automatically recommends new versions of features and feature lists linked to the affected tables, ensuring that future models and deployments use updated versions that address data integrity or freshness issues.
For a deeper dive into FeatureByte Enterprise, see:
In [93]:
Copied!
import featurebyte as fb
import pandas as pd
# Set your profile to the tutorial environment
fb.use_profile("tutorial")
catalog_name = "Credit Default Dataset SDK Tutorial"
catalog = fb.Catalog.activate(catalog_name)
import featurebyte as fb
import pandas as pd
# Set your profile to the tutorial environment
fb.use_profile("tutorial")
catalog_name = "Credit Default Dataset SDK Tutorial"
catalog = fb.Catalog.activate(catalog_name)
17:18:32 | INFO | Using profile: staging INFO :featurebyte:Using profile: staging 17:18:32 | INFO | Using configuration file at: /Users/gxav/.featurebyte/config.yaml INFO :featurebyte:Using configuration file at: /Users/gxav/.featurebyte/config.yaml 17:18:32 | INFO | Active profile: staging (https://staging.featurebyte.com/api/v1) INFO :featurebyte:Active profile: staging (https://staging.featurebyte.com/api/v1) 17:18:32 | INFO | SDK version: 3.2.0.dev66 INFO :featurebyte:SDK version: 3.2.0.dev66 17:18:32 | INFO | No catalog activated. INFO :featurebyte:No catalog activated. 17:18:32 | INFO | Catalog activated: Credit Default Dataset SDK Tutorial INFO :featurebyte.api.catalog:Catalog activated: Credit Default Dataset SDK Tutorial 16:16:27 | INFO | Active profile: tutorial (https://tutorials.featurebyte.com/api/v1) 16:16:27 | WARNING | Remote SDK version (1.1.0.dev7) is different from local (1.1.0.dev1). Update local SDK to avoid unexpected behavior. 16:16:27 | INFO | No catalog activated. 16:16:27 | INFO | Catalog activated: Grocery Dataset Tutorial
Update Default Feature Job Setting at the table level¶
In [94]:
Copied!
# Get BUREAU table
bureau_table = catalog.get_table("BUREAU")
# Get BUREAU table
bureau_table = catalog.get_table("BUREAU")
In [95]:
Copied!
# Get current Default Feature Job Setting
bureau_table.default_feature_job_setting
# Get current Default Feature Job Setting
bureau_table.default_feature_job_setting
Out[95]:
FeatureJobSetting(blind_spot='25800s', period='86400s', offset='25740s', execution_buffer='0s')
In [96]:
Copied!
# List past analysis
past_analysis = bureau_table.list_feature_job_setting_analysis()
# List past analysis
past_analysis = bureau_table.list_feature_job_setting_analysis()
In [97]:
Copied!
# Get past analysis
analysis_id = past_analysis.id.to_list()[0]
analysis = fb.FeatureJobSettingAnalysis.get_by_id(analysis_id)
# Get past analysis
analysis_id = past_analysis.id.to_list()[0]
analysis = fb.FeatureJobSettingAnalysis.get_by_id(analysis_id)
In [98]:
Copied!
# Backtest new setting
new_feature_job_setting = fb.FeatureJobSetting(
blind_spot='28800s',
period=bureau_table.default_feature_job_setting.period,
offset=bureau_table.default_feature_job_setting.offset
)
backtest_result = analysis.backtest(feature_job_setting=new_feature_job_setting)
# Backtest new setting
new_feature_job_setting = fb.FeatureJobSetting(
blind_spot='28800s',
period=bureau_table.default_feature_job_setting.period,
offset=bureau_table.default_feature_job_setting.offset
)
backtest_result = analysis.backtest(feature_job_setting=new_feature_job_setting)
Done! |████████████████████████████████████████| 100% in 6.1s (0.17%/s)
Feature Job Setting Analysis Report
Backtest Result
For the feature job setting:
- Period = 86400 s / Offset = 25740 s / Blind spot = 28800 s
The backtest found that all records would have been processed on time.
- Period = 86400 s / Offset = 25740 s / Blind spot = 28800 s
The backtest found that all records would have been processed on time.
Done! |████████████████████████████████████████| 100% in 6.1s (0.17%/s)
Feature Job Setting Analysis Report
Backtest Result
For the feature job setting:
- Period = 3600 s / Offset = 120 s / Blind spot = 240 s
The backtest found that all records would have been processed on time.
- Period = 3600 s / Offset = 120 s / Blind spot = 240 s
The backtest found that all records would have been processed on time.
In [99]:
Copied!
# Update Default Feature Job Setting
bureau_table.update_default_feature_job_setting(new_feature_job_setting)
# Update Default Feature Job Setting
bureau_table.update_default_feature_job_setting(new_feature_job_setting)
Note:
- This new Default Feature Job Setting will be used by default by any new feature using the table.
- In the Enterprise platform, an approval process is associated with the change in the Default Feature Job Setting and a request to create new versions of existing features is automatically triggered.
Update Default Cleaning Operations at the table level¶
In [100]:
Copied!
# Get BUREAU table
bureau_table = catalog.get_table("BUREAU")
# Get BUREAU table
bureau_table = catalog.get_table("BUREAU")
In [101]:
Copied!
# Get Info on columns
columns_info = pd.DataFrame(bureau_table.info(verbose=True)['columns_info'])
display(columns_info)
# Get Info on columns
columns_info = pd.DataFrame(bureau_table.info(verbose=True)['columns_info'])
display(columns_info)
name | dtype | entity | semantic | critical_data_info | description | |
---|---|---|---|---|---|---|
0 | ClientID | INT | Client | None | None | ID of the client |
1 | SK_ID_BUREAU | INT | BureauReportedCredit | event_id | None | Recoded ID of previous Credit Bureau credit re... |
2 | CREDIT_ACTIVE | VARCHAR | None | None | None | Status of the Credit Bureau (CB) reported credits |
3 | CREDIT_CURRENCY | VARCHAR | None | None | None | Recoded currency of the Credit Bureau credit |
4 | bureau_application_time | TIMESTAMP | None | None | None | Timestamp when client applied prior applicatio... |
5 | CREDIT_DAY_OVERDUE | INT | None | None | None | Number of days past due on CB credit at the ti... |
6 | credit_end_date | TIMESTAMP | None | None | None | When prior CB credit is scheduled to end |
7 | credit_end_fact | TIMESTAMP | None | None | None | When prior CB credit ended (only for closed cr... |
8 | AMT_CREDIT_MAX_OVERDUE | FLOAT | None | None | None | Maximal amount overdue on the Credit Bureau cr... |
9 | CNT_CREDIT_PROLONG | INT | None | None | None | How many times was the Credit Bureau credit pr... |
10 | AMT_CREDIT_SUM | FLOAT | None | None | None | Current credit amount for the Credit Bureau cr... |
11 | AMT_CREDIT_SUM_DEBT | FLOAT | None | None | None | Current debt on Credit Bureau credit |
12 | AMT_CREDIT_SUM_LIMIT | FLOAT | None | None | None | Current credit limit of credit card reported i... |
13 | AMT_CREDIT_SUM_OVERDUE | FLOAT | None | None | None | Current amount overdue on Credit Bureau credit |
14 | CREDIT_TYPE | VARCHAR | None | None | None | Type of Credit Bureau credit (Car, cash,...) |
15 | credit_update | TIMESTAMP | None | event_timestamp | None | Timestamp when did last information about the ... |
16 | AMT_ANNUITY | FLOAT | None | None | None | Annuity of the Credit Bureau credit |
17 | available_at | TIMESTAMP | None | record_creation_timestamp | None | Timestamp the record was added to the database |
In [102]:
Copied!
# Get Current Cleaning Operation for Amount column
for info in columns_info.loc[columns_info.name=="AMT_CREDIT_SUM_DEBT"]["critical_data_info"]:
print(info)
# Get Current Cleaning Operation for Amount column
for info in columns_info.loc[columns_info.name=="AMT_CREDIT_SUM_DEBT"]["critical_data_info"]:
print(info)
None
In [103]:
Copied!
# Update Cleaning Operations by handling disguised missing values for AMT_CREDIT_SUM_DEBT
new_cleaning_operations = [
fb.DisguisedValueImputation(disguised_values=[-99, -98, -96], imputed_value=None),
]
bureau_table["AMT_CREDIT_SUM_DEBT"].update_critical_data_info(
cleaning_operations=new_cleaning_operations
)
# Update Cleaning Operations by handling disguised missing values for AMT_CREDIT_SUM_DEBT
new_cleaning_operations = [
fb.DisguisedValueImputation(disguised_values=[-99, -98, -96], imputed_value=None),
]
bureau_table["AMT_CREDIT_SUM_DEBT"].update_critical_data_info(
cleaning_operations=new_cleaning_operations
)
Note:
- This new Default Cleaning Operations will be used by default by any new feature using the table column.
- In the Enterprise platform, an approval process is associated with the change in the Default Cleaning Operations and a request to create new versions of existing features using the table column is automatically triggered.
Detect features with discrepancies¶
In [104]:
Copied!
# Define target table
table_name = "BUREAU"
table = catalog.get_table(table_name)
default_job_setting = table.default_feature_job_setting.model_dump()
# Prepare lists for discrepancies
cleaning_discrepancies = []
job_discrepancies = []
# Iterate through all features in the catalog
for feature_name in catalog.list_features()["name"].to_list():
feature = catalog.get_feature(feature_name)
feature_info = feature.info()
# --- Compare cleaning operations ---
default_cleaning = {}
actual_cleaning = {}
# Collect default cleaning operations from the table definition
for input_col in feature_info["metadata"]["input_columns"].values():
for col_op in table.column_cleaning_operations:
col_op = col_op.model_dump()
if col_op["column_name"] == input_col["column_name"]:
default_cleaning[col_op["column_name"]] = col_op["cleaning_operations"]
break
# Collect actual cleaning operations used by the feature
for table_cleaning in feature_info["table_cleaning_operation"]["this"]:
if table_cleaning["table_name"] == table_name:
for col_op in table_cleaning["column_cleaning_operations"]:
actual_cleaning[col_op["column_name"]] = col_op["cleaning_operations"]
# Record discrepancies in cleaning definitions
if default_cleaning != actual_cleaning:
cleaning_discrepancies.append(feature_name)
# --- Compare feature job settings ---
for job_info in feature_info["table_feature_job_setting"]["this"]:
if (
job_info["table_name"] == table_name
and job_info["feature_job_setting"] != default_job_setting
):
job_discrepancies.append(feature_name)
# --- Summary ---
print("Features with cleaning discrepancies:", cleaning_discrepancies)
print("Features with job discrepancies:", job_discrepancies)
# Define target table
table_name = "BUREAU"
table = catalog.get_table(table_name)
default_job_setting = table.default_feature_job_setting.model_dump()
# Prepare lists for discrepancies
cleaning_discrepancies = []
job_discrepancies = []
# Iterate through all features in the catalog
for feature_name in catalog.list_features()["name"].to_list():
feature = catalog.get_feature(feature_name)
feature_info = feature.info()
# --- Compare cleaning operations ---
default_cleaning = {}
actual_cleaning = {}
# Collect default cleaning operations from the table definition
for input_col in feature_info["metadata"]["input_columns"].values():
for col_op in table.column_cleaning_operations:
col_op = col_op.model_dump()
if col_op["column_name"] == input_col["column_name"]:
default_cleaning[col_op["column_name"]] = col_op["cleaning_operations"]
break
# Collect actual cleaning operations used by the feature
for table_cleaning in feature_info["table_cleaning_operation"]["this"]:
if table_cleaning["table_name"] == table_name:
for col_op in table_cleaning["column_cleaning_operations"]:
actual_cleaning[col_op["column_name"]] = col_op["cleaning_operations"]
# Record discrepancies in cleaning definitions
if default_cleaning != actual_cleaning:
cleaning_discrepancies.append(feature_name)
# --- Compare feature job settings ---
for job_info in feature_info["table_feature_job_setting"]["this"]:
if (
job_info["table_name"] == table_name
and job_info["feature_job_setting"] != default_job_setting
):
job_discrepancies.append(feature_name)
# --- Summary ---
print("Features with cleaning discrepancies:", cleaning_discrepancies)
print("Features with job discrepancies:", job_discrepancies)
Features with cleaning discrepancies: ['CLIENT_Avg_of_BureauReportedCredits_Available_Credits_104w', 'CLIENT_Avg_of_BureauReportedCredits_Available_Credits_26w', 'CLIENT_Max_of_Active_Cr_active_BureauReportedCredits_AMT_CREDIT_SUM_DEBT_To_AMT_CREDIT_SUMs_104w', 'CLIENT_Max_of_Consumer_credit_Cr_type_BureauReportedCredits_AMT_CREDIT_SUM_DEBT_To_AMT_CREDIT_SUMs_52w', 'CLIENT_Std_of_BureauReportedCredits_Available_Credits_26w'] Features with job discrepancies: ['CLIENT_Avg_of_BureauReportedCredits_Available_Credits_104w', 'CLIENT_Avg_of_BureauReportedCredits_Available_Credits_26w', 'CLIENT_Avg_of_Consumer_credit_Cr_type_BureauReportedCredits_AMT_CREDIT_SUMs_104w', 'CLIENT_Avg_of_Consumer_credit_Cr_type_BureauReportedCredits_End_to_Update_Gaps_104w', 'CLIENT_Max_of_Active_Cr_active_BureauReportedCredits_AMT_CREDIT_SUM_DEBT_To_AMT_CREDIT_SUMs_104w', 'CLIENT_Max_of_Consumer_credit_Cr_type_BureauReportedCredits_AMT_CREDIT_SUM_DEBT_To_AMT_CREDIT_SUMs_52w', 'CLIENT_Std_of_BureauReportedCredits_Available_Credits_26w']
Change feature job setting and cleaning operations of features with discrepancies¶
In [105]:
Copied!
for feature_name in list(set(cleaning_discrepancies + job_discrepancies)):
feature = catalog.get_feature(feature_name)
feature_info = feature.info()
feature.update_readiness("DEPRECATED")
new_cleanings = []
if feature_name in cleaning_discrepancies:
table_cleaning_updated = False
else:
table_cleaning_updated = True
for cleaning_info in feature_info["table_cleaning_operation"]["this"]:
if not table_cleaning_updated and cleaning_info["table_name"] == table_name:
new_cleanings.append(
fb.TableCleaningOperation(
table_name=table_name,
column_cleaning_operations=table.column_cleaning_operations,
)
)
table_cleaning_updated = True
else:
column_cleaning_operations = [
fb.ColumnCleaningOperation(op)
for op in cleaning_info["cleaning_operations"]
]
new_cleanings.append(
fb.TableCleaningOperation(
table_name=cleaning_info["table_name"],
column_cleaning_operations=column_cleaning_operations)
)
if not table_cleaning_updated:
new_cleanings.append(
fb.TableCleaningOperation(
table_name=table_name,
column_cleaning_operations=table.column_cleaning_operations,
)
)
new_feature_job_settings = []
for job_info in feature_info["table_feature_job_setting"]["this"]:
if feature_name in job_discrepancies and job_info["table_name"] == table_name:
new_feature_job_settings.append(
fb.TableFeatureJobSetting(
table_name=table_name,
feature_job_setting=table.default_feature_job_setting,
)
)
else:
new_feature_job_settings.append(
fb.TableFeatureJobSetting(
table_name=job_info["table_name"],
feature_job_setting=fb.FeatureJobSetting(**job_info["feature_job_setting"]),
)
)
new_version = feature.create_new_version(
table_feature_job_settings=new_feature_job_settings,
table_cleaning_operations=new_cleanings,
)
new_version.update_readiness("PRODUCTION_READY")
for feature_name in list(set(cleaning_discrepancies + job_discrepancies)):
feature = catalog.get_feature(feature_name)
feature_info = feature.info()
feature.update_readiness("DEPRECATED")
new_cleanings = []
if feature_name in cleaning_discrepancies:
table_cleaning_updated = False
else:
table_cleaning_updated = True
for cleaning_info in feature_info["table_cleaning_operation"]["this"]:
if not table_cleaning_updated and cleaning_info["table_name"] == table_name:
new_cleanings.append(
fb.TableCleaningOperation(
table_name=table_name,
column_cleaning_operations=table.column_cleaning_operations,
)
)
table_cleaning_updated = True
else:
column_cleaning_operations = [
fb.ColumnCleaningOperation(op)
for op in cleaning_info["cleaning_operations"]
]
new_cleanings.append(
fb.TableCleaningOperation(
table_name=cleaning_info["table_name"],
column_cleaning_operations=column_cleaning_operations)
)
if not table_cleaning_updated:
new_cleanings.append(
fb.TableCleaningOperation(
table_name=table_name,
column_cleaning_operations=table.column_cleaning_operations,
)
)
new_feature_job_settings = []
for job_info in feature_info["table_feature_job_setting"]["this"]:
if feature_name in job_discrepancies and job_info["table_name"] == table_name:
new_feature_job_settings.append(
fb.TableFeatureJobSetting(
table_name=table_name,
feature_job_setting=table.default_feature_job_setting,
)
)
else:
new_feature_job_settings.append(
fb.TableFeatureJobSetting(
table_name=job_info["table_name"],
feature_job_setting=fb.FeatureJobSetting(**job_info["feature_job_setting"]),
)
)
new_version = feature.create_new_version(
table_feature_job_settings=new_feature_job_settings,
table_cleaning_operations=new_cleanings,
)
new_version.update_readiness("PRODUCTION_READY")
Create new version of a feature list¶
In [106]:
Copied!
# Get feature list from catalog
feature_list = catalog.get_feature_list("40 features for Credit Default")
# Get feature list from catalog
feature_list = catalog.get_feature_list("40 features for Credit Default")
Loading Feature(s) |████████████████████████████████████████| 40/40 [100%] in 0. Loading Feature(s) |████████████████████████████████████████| 9/9 [100%] in 0.2s
In [107]:
Copied!
# Check Fraction of default features and production_ready features
print(f"default_feature_fraction: {feature_list.default_feature_fraction}")
print(f"production_ready_fraction: {feature_list.production_ready_fraction}")
# Check Fraction of default features and production_ready features
print(f"default_feature_fraction: {feature_list.default_feature_fraction}")
print(f"production_ready_fraction: {feature_list.production_ready_fraction}")
default_feature_fraction: 0.825 production_ready_fraction: 0.825
In [108]:
Copied!
# Create new version
new_version = feature_list.create_new_version()
# Create new version
new_version = feature_list.create_new_version()
Loading Feature(s) |████████████████████████████████████████| 40/40 [100%] in 0. Loading Feature(s) |████████████████████████████████████████| 9/9 [100%] in 0.4s
In [109]:
Copied!
# Check Fraction of default features of new version
print(f"default_feature_fraction: {new_version.default_feature_fraction}")
print(f"production_ready_fraction: {new_version.production_ready_fraction}")
# Check Fraction of default features of new version
print(f"default_feature_fraction: {new_version.default_feature_fraction}")
print(f"production_ready_fraction: {new_version.production_ready_fraction}")
default_feature_fraction: 1.0 production_ready_fraction: 1.0
In [110]:
Copied!
# Check new version is the default
print(
f"version_name: {new_version.version} \n",
f"is the new version the new default? {new_version.is_default}"
)
# Check new version is the default
print(
f"version_name: {new_version.version} \n",
f"is the new version the new default? {new_version.is_default}"
)
version_name: V251015_1 is the new version the new default? True
In [111]:
Copied!
# Get Default version from Catalog
feature_list = catalog.get_feature_list("40 features for Credit Default")
print(feature_list.version)
# Get Default version from Catalog
feature_list = catalog.get_feature_list("40 features for Credit Default")
print(feature_list.version)
Loading Feature(s) |████████████████████████████████████████| 40/40 [100%] in 0. V251015_1 Loading Feature(s) |████████████████████████████████████████| 9/9 [100%] in 0.4s V240612_1
In [112]:
Copied!
# List versions
feature_list.list_versions()
# List versions
feature_list.list_versions()
Out[112]:
id | name | version | online_frac | deployed | created_at | is_default | |
---|---|---|---|---|---|---|---|
0 | 68ef6709b5826853a69c9cb8 | 40 features for Credit Default | V251015_1 | 0.0 | False | 2025-10-15T09:19:05.964000 | True |
1 | 68ef63333e2dac375b630de7 | 40 features for Credit Default | V251015 | 0.0 | False | 2025-10-15T09:02:51.314000 | False |
In [113]:
Copied!
# Check Production Readiness
feature_list.production_ready_fraction
# Check Production Readiness
feature_list.production_ready_fraction
Out[113]:
1.0
Delete / Deprecate features and feature lists¶
Only 'DRAFT' features that are not part of a feature list or 'DRAFT' feature list can be deleted. For others, deprecate them instead.
In [114]:
Copied!
feature = catalog.get_feature("CLIENT_Avg_of_BureauReportedCredits_Available_Credits_26w")
feature = catalog.get_feature("CLIENT_Avg_of_BureauReportedCredits_Available_Credits_26w")
In [115]:
Copied!
# Check that guardrails prevent you from deleting a feature that is production_ready
try:
feature.delete()
except Exception as e:
print(e)
# Check that guardrails prevent you from deleting a feature that is production_ready
try:
feature.delete()
except Exception as e:
print(e)
('Only feature with draft readiness can be deleted.', 'Failed to delete specified object.')
In [116]:
Copied!
# Deprecate it
feature.update_readiness("DEPRECATED")
# Deprecate it
feature.update_readiness("DEPRECATED")
Done! |████████████████████████████████████████| 100% in 6.1s (0.17%/s) Done! |████████████████████████████████████████| 100% in 6.1s (0.17%/s) Loading Feature(s) |████████████████████████████████████████| 2/2 [100%] in 0.2s
In [117]:
Copied!
# Delete feature list
try:
feature_list.delete()
except Exception as e:
print(e)
# Delete feature list
try:
feature_list.delete()
except Exception as e:
print(e)
('Only feature list with DRAFT status can be deleted.', 'Failed to delete specified object.')
In [118]:
Copied!
feature_list.update_status("DEPRECATED")
feature_list.update_status("DEPRECATED")
Concepts in this tutorial¶
- Feature versioning
- Default feature version
- Feature list versioning
- Default Feature Job Setting
- Feature Job Setting Recommendation
- Default Cleaning Operations
SDK reference for¶
- Feature
- FeatureList
- Feature.readiness
- Feature.update_readiness()
- Feature.info()
- EventTable.list_feature_job_setting_analysis()
- EventTable.create_new_feature_job_setting_analysis()
- EventTable.update_default_feature_job_setting()
- FeatureJobSettingAnalysis.backtest()
- TableColumn.update_critical_data_info()
- Feature.create_new_version()
- Feature.list_versions()
- Feature.delete()
- FeatureList.create_new_version()
- FeatureList.list_versions()
- FeatureList.delete()
- FeatureList.production_ready_fraction
- FeatureList.default_feature_fraction
In [ ]:
Copied!
In [ ]:
Copied!