Adding get_feature_names to ColumnTransformer pipeline
up vote
0
down vote
favorite
I'm trying to create an sklearn.compose.ColumnTransformer pipeline for transforming both categorical and continuous input data:
import pandas as pd
from sklearn.base import TransformerMixin, BaseEstimator
from sklearn.preprocessing import OneHotEncoder, FunctionTransformer
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer, make_column_transformer
from sklearn.impute import SimpleImputer
df = pd.DataFrame(
{
'a': [1, 'a', 1, np.nan, 'b'],
'b': [1, 2, 3, 4, 5],
'c': list('abcde'),
'd': list('aaabb'),
'e': [0, 1, 1, 0, 1],
}
)
for col in df.select_dtypes('object'):
df[col] = df[col].astype(str)
categorical_columns = list('acd')
continuous_columns = list('be')
categorical_transformer = OneHotEncoder(sparse=False, handle_unknown='ignore')
continuous_transformer = 'passthrough'
column_transformer = ColumnTransformer(
[
('categorical', categorical_transformer, categorical_columns),
('continuous', continuous_transformer, continuous_columns),
]
,
sparse_threshold=0.,
n_jobs=-1
)
X = column_transformer.fit_transform(df)
I want to access the feature names created by this transformation pipeline, so I try this:
column_transformer.get_feature_names()
Which raises:
NotImplementedError: get_feature_names is not yet supported when using a 'passthrough' transformer.
Since I'm not technically doing anything with columns b and e, I technically could just append them onto X after one-hot encoding all other features, but is there some way I can use one of the scikit base classes (e.g. TransformerMixin, BaseEstimator, or FunctionTransformer) to add to this pipeline so I can grab the continuous feature names in a very pipeline-friendly way?
Something like this, perhaps:
class PassthroughTransformer(FunctionTransformer, BaseEstimator):
def fit(self):
return self
def transform(self, X)
self.X = X
return X
def get_feature_names(self):
return self.X.values.tolist()
continuous_transformer = PassthroughTransformer()
column_transformer = ColumnTransformer(
[
('categorical', categorical_transformer, categorical_columns),
('continuous', continuous_transformer, continuous_columns),
]
,
sparse_threshold=0.,
n_jobs=-1
)
X = column_transformer.fit_transform(df)
But this raises this exception:
TypeError: Cannot clone object '<__main__.PassthroughTransformer object at 0x1132ddf60>' (type <class '__main__.PassthroughTransformer'>): it does not seem to be a scikit-learn estimator as it does not implement a 'get_params' methods.
python scikit-learn
add a comment |
up vote
0
down vote
favorite
I'm trying to create an sklearn.compose.ColumnTransformer pipeline for transforming both categorical and continuous input data:
import pandas as pd
from sklearn.base import TransformerMixin, BaseEstimator
from sklearn.preprocessing import OneHotEncoder, FunctionTransformer
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer, make_column_transformer
from sklearn.impute import SimpleImputer
df = pd.DataFrame(
{
'a': [1, 'a', 1, np.nan, 'b'],
'b': [1, 2, 3, 4, 5],
'c': list('abcde'),
'd': list('aaabb'),
'e': [0, 1, 1, 0, 1],
}
)
for col in df.select_dtypes('object'):
df[col] = df[col].astype(str)
categorical_columns = list('acd')
continuous_columns = list('be')
categorical_transformer = OneHotEncoder(sparse=False, handle_unknown='ignore')
continuous_transformer = 'passthrough'
column_transformer = ColumnTransformer(
[
('categorical', categorical_transformer, categorical_columns),
('continuous', continuous_transformer, continuous_columns),
]
,
sparse_threshold=0.,
n_jobs=-1
)
X = column_transformer.fit_transform(df)
I want to access the feature names created by this transformation pipeline, so I try this:
column_transformer.get_feature_names()
Which raises:
NotImplementedError: get_feature_names is not yet supported when using a 'passthrough' transformer.
Since I'm not technically doing anything with columns b and e, I technically could just append them onto X after one-hot encoding all other features, but is there some way I can use one of the scikit base classes (e.g. TransformerMixin, BaseEstimator, or FunctionTransformer) to add to this pipeline so I can grab the continuous feature names in a very pipeline-friendly way?
Something like this, perhaps:
class PassthroughTransformer(FunctionTransformer, BaseEstimator):
def fit(self):
return self
def transform(self, X)
self.X = X
return X
def get_feature_names(self):
return self.X.values.tolist()
continuous_transformer = PassthroughTransformer()
column_transformer = ColumnTransformer(
[
('categorical', categorical_transformer, categorical_columns),
('continuous', continuous_transformer, continuous_columns),
]
,
sparse_threshold=0.,
n_jobs=-1
)
X = column_transformer.fit_transform(df)
But this raises this exception:
TypeError: Cannot clone object '<__main__.PassthroughTransformer object at 0x1132ddf60>' (type <class '__main__.PassthroughTransformer'>): it does not seem to be a scikit-learn estimator as it does not implement a 'get_params' methods.
python scikit-learn
add a comment |
up vote
0
down vote
favorite
up vote
0
down vote
favorite
I'm trying to create an sklearn.compose.ColumnTransformer pipeline for transforming both categorical and continuous input data:
import pandas as pd
from sklearn.base import TransformerMixin, BaseEstimator
from sklearn.preprocessing import OneHotEncoder, FunctionTransformer
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer, make_column_transformer
from sklearn.impute import SimpleImputer
df = pd.DataFrame(
{
'a': [1, 'a', 1, np.nan, 'b'],
'b': [1, 2, 3, 4, 5],
'c': list('abcde'),
'd': list('aaabb'),
'e': [0, 1, 1, 0, 1],
}
)
for col in df.select_dtypes('object'):
df[col] = df[col].astype(str)
categorical_columns = list('acd')
continuous_columns = list('be')
categorical_transformer = OneHotEncoder(sparse=False, handle_unknown='ignore')
continuous_transformer = 'passthrough'
column_transformer = ColumnTransformer(
[
('categorical', categorical_transformer, categorical_columns),
('continuous', continuous_transformer, continuous_columns),
]
,
sparse_threshold=0.,
n_jobs=-1
)
X = column_transformer.fit_transform(df)
I want to access the feature names created by this transformation pipeline, so I try this:
column_transformer.get_feature_names()
Which raises:
NotImplementedError: get_feature_names is not yet supported when using a 'passthrough' transformer.
Since I'm not technically doing anything with columns b and e, I technically could just append them onto X after one-hot encoding all other features, but is there some way I can use one of the scikit base classes (e.g. TransformerMixin, BaseEstimator, or FunctionTransformer) to add to this pipeline so I can grab the continuous feature names in a very pipeline-friendly way?
Something like this, perhaps:
class PassthroughTransformer(FunctionTransformer, BaseEstimator):
def fit(self):
return self
def transform(self, X)
self.X = X
return X
def get_feature_names(self):
return self.X.values.tolist()
continuous_transformer = PassthroughTransformer()
column_transformer = ColumnTransformer(
[
('categorical', categorical_transformer, categorical_columns),
('continuous', continuous_transformer, continuous_columns),
]
,
sparse_threshold=0.,
n_jobs=-1
)
X = column_transformer.fit_transform(df)
But this raises this exception:
TypeError: Cannot clone object '<__main__.PassthroughTransformer object at 0x1132ddf60>' (type <class '__main__.PassthroughTransformer'>): it does not seem to be a scikit-learn estimator as it does not implement a 'get_params' methods.
python scikit-learn
I'm trying to create an sklearn.compose.ColumnTransformer pipeline for transforming both categorical and continuous input data:
import pandas as pd
from sklearn.base import TransformerMixin, BaseEstimator
from sklearn.preprocessing import OneHotEncoder, FunctionTransformer
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer, make_column_transformer
from sklearn.impute import SimpleImputer
df = pd.DataFrame(
{
'a': [1, 'a', 1, np.nan, 'b'],
'b': [1, 2, 3, 4, 5],
'c': list('abcde'),
'd': list('aaabb'),
'e': [0, 1, 1, 0, 1],
}
)
for col in df.select_dtypes('object'):
df[col] = df[col].astype(str)
categorical_columns = list('acd')
continuous_columns = list('be')
categorical_transformer = OneHotEncoder(sparse=False, handle_unknown='ignore')
continuous_transformer = 'passthrough'
column_transformer = ColumnTransformer(
[
('categorical', categorical_transformer, categorical_columns),
('continuous', continuous_transformer, continuous_columns),
]
,
sparse_threshold=0.,
n_jobs=-1
)
X = column_transformer.fit_transform(df)
I want to access the feature names created by this transformation pipeline, so I try this:
column_transformer.get_feature_names()
Which raises:
NotImplementedError: get_feature_names is not yet supported when using a 'passthrough' transformer.
Since I'm not technically doing anything with columns b and e, I technically could just append them onto X after one-hot encoding all other features, but is there some way I can use one of the scikit base classes (e.g. TransformerMixin, BaseEstimator, or FunctionTransformer) to add to this pipeline so I can grab the continuous feature names in a very pipeline-friendly way?
Something like this, perhaps:
class PassthroughTransformer(FunctionTransformer, BaseEstimator):
def fit(self):
return self
def transform(self, X)
self.X = X
return X
def get_feature_names(self):
return self.X.values.tolist()
continuous_transformer = PassthroughTransformer()
column_transformer = ColumnTransformer(
[
('categorical', categorical_transformer, categorical_columns),
('continuous', continuous_transformer, continuous_columns),
]
,
sparse_threshold=0.,
n_jobs=-1
)
X = column_transformer.fit_transform(df)
But this raises this exception:
TypeError: Cannot clone object '<__main__.PassthroughTransformer object at 0x1132ddf60>' (type <class '__main__.PassthroughTransformer'>): it does not seem to be a scikit-learn estimator as it does not implement a 'get_params' methods.
python scikit-learn
python scikit-learn
asked Nov 19 at 20:40
blacksite
5,22041643
5,22041643
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
up vote
2
down vote
accepted
There are multiple issues here:
Cannot clone object error is due to parallel processing:
By default,
scikit-learnclones (which usespickle) the supplied transformers and estimators when its working in Pipeline and similar (FeatureUnion, ColumnTransformer etc) or in cross-validation (cross_val_score,GridSearchCVetc).
Now, you have specified
n_jobs=-1in yourColumnTransformer, which introduces multiprocessing in the code. Python's inbuilt pickling dont work well with multiprocessing. And hence the error.
Options:
Set
n_jobs = 1to not use multiprocessing. Still need to correct the code according to point 2 and 3.If you want to use multiprocessing, then simplest solution is to define the custom classes in a separate file (module) and import it into your main file. Something like this:
Make a new file in same folder named custom_transformers.py with contents:
from sklearn.base import TransformerMixin, BaseEstimator
# Changed the base classes here, see Point 3
class PassthroughTransformer(BaseEstimator, TransformerMixin):
# I corrected the `fit()` method here, it should take X, y as input
def fit(self, X, y=None):
return self
def transform(self, X):
self.X = X
return X
# I have corrected the output here, See point 2
def get_feature_names(self):
return self.X.columns.tolist()
Now in your main file, do this:
from custom_transformers import PassthroughTransformer
For more information, see these questions:
- Python multiprocessing pickling error
- Python: Can't pickle type X, attribute lookup failed
Custom sklearn pipeline transformer giving "pickle.PicklingError" (I am suggesting this workaround)
You return
self.X.values.tolist():-
Here
Xis a PandasDataFrame, soX.values.tolist()will return the actual data of the columns you specify, not the column names. So even if you solve the first error, you will get error in this. Correct this to:
return self.X.columns.tolist()
(Minor) Class inheriting:
You defined the PassthroughTransformer as:
PassthroughTransformer(FunctionTransformer, BaseEstimator)
FunctionTransformeralready inherits fromBaseEstimatorso I dont think there is need to inherit fromBaseEstimator. You can change it in following ways:
class PassthroughTransformer(FunctionTransformer):
OR
# Standard way
class PassthroughTransformer(BaseEstimator, TransformerMixin):
Hope this helps.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53382322%2fadding-get-feature-names-to-columntransformer-pipeline%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
2
down vote
accepted
There are multiple issues here:
Cannot clone object error is due to parallel processing:
By default,
scikit-learnclones (which usespickle) the supplied transformers and estimators when its working in Pipeline and similar (FeatureUnion, ColumnTransformer etc) or in cross-validation (cross_val_score,GridSearchCVetc).
Now, you have specified
n_jobs=-1in yourColumnTransformer, which introduces multiprocessing in the code. Python's inbuilt pickling dont work well with multiprocessing. And hence the error.
Options:
Set
n_jobs = 1to not use multiprocessing. Still need to correct the code according to point 2 and 3.If you want to use multiprocessing, then simplest solution is to define the custom classes in a separate file (module) and import it into your main file. Something like this:
Make a new file in same folder named custom_transformers.py with contents:
from sklearn.base import TransformerMixin, BaseEstimator
# Changed the base classes here, see Point 3
class PassthroughTransformer(BaseEstimator, TransformerMixin):
# I corrected the `fit()` method here, it should take X, y as input
def fit(self, X, y=None):
return self
def transform(self, X):
self.X = X
return X
# I have corrected the output here, See point 2
def get_feature_names(self):
return self.X.columns.tolist()
Now in your main file, do this:
from custom_transformers import PassthroughTransformer
For more information, see these questions:
- Python multiprocessing pickling error
- Python: Can't pickle type X, attribute lookup failed
Custom sklearn pipeline transformer giving "pickle.PicklingError" (I am suggesting this workaround)
You return
self.X.values.tolist():-
Here
Xis a PandasDataFrame, soX.values.tolist()will return the actual data of the columns you specify, not the column names. So even if you solve the first error, you will get error in this. Correct this to:
return self.X.columns.tolist()
(Minor) Class inheriting:
You defined the PassthroughTransformer as:
PassthroughTransformer(FunctionTransformer, BaseEstimator)
FunctionTransformeralready inherits fromBaseEstimatorso I dont think there is need to inherit fromBaseEstimator. You can change it in following ways:
class PassthroughTransformer(FunctionTransformer):
OR
# Standard way
class PassthroughTransformer(BaseEstimator, TransformerMixin):
Hope this helps.
add a comment |
up vote
2
down vote
accepted
There are multiple issues here:
Cannot clone object error is due to parallel processing:
By default,
scikit-learnclones (which usespickle) the supplied transformers and estimators when its working in Pipeline and similar (FeatureUnion, ColumnTransformer etc) or in cross-validation (cross_val_score,GridSearchCVetc).
Now, you have specified
n_jobs=-1in yourColumnTransformer, which introduces multiprocessing in the code. Python's inbuilt pickling dont work well with multiprocessing. And hence the error.
Options:
Set
n_jobs = 1to not use multiprocessing. Still need to correct the code according to point 2 and 3.If you want to use multiprocessing, then simplest solution is to define the custom classes in a separate file (module) and import it into your main file. Something like this:
Make a new file in same folder named custom_transformers.py with contents:
from sklearn.base import TransformerMixin, BaseEstimator
# Changed the base classes here, see Point 3
class PassthroughTransformer(BaseEstimator, TransformerMixin):
# I corrected the `fit()` method here, it should take X, y as input
def fit(self, X, y=None):
return self
def transform(self, X):
self.X = X
return X
# I have corrected the output here, See point 2
def get_feature_names(self):
return self.X.columns.tolist()
Now in your main file, do this:
from custom_transformers import PassthroughTransformer
For more information, see these questions:
- Python multiprocessing pickling error
- Python: Can't pickle type X, attribute lookup failed
Custom sklearn pipeline transformer giving "pickle.PicklingError" (I am suggesting this workaround)
You return
self.X.values.tolist():-
Here
Xis a PandasDataFrame, soX.values.tolist()will return the actual data of the columns you specify, not the column names. So even if you solve the first error, you will get error in this. Correct this to:
return self.X.columns.tolist()
(Minor) Class inheriting:
You defined the PassthroughTransformer as:
PassthroughTransformer(FunctionTransformer, BaseEstimator)
FunctionTransformeralready inherits fromBaseEstimatorso I dont think there is need to inherit fromBaseEstimator. You can change it in following ways:
class PassthroughTransformer(FunctionTransformer):
OR
# Standard way
class PassthroughTransformer(BaseEstimator, TransformerMixin):
Hope this helps.
add a comment |
up vote
2
down vote
accepted
up vote
2
down vote
accepted
There are multiple issues here:
Cannot clone object error is due to parallel processing:
By default,
scikit-learnclones (which usespickle) the supplied transformers and estimators when its working in Pipeline and similar (FeatureUnion, ColumnTransformer etc) or in cross-validation (cross_val_score,GridSearchCVetc).
Now, you have specified
n_jobs=-1in yourColumnTransformer, which introduces multiprocessing in the code. Python's inbuilt pickling dont work well with multiprocessing. And hence the error.
Options:
Set
n_jobs = 1to not use multiprocessing. Still need to correct the code according to point 2 and 3.If you want to use multiprocessing, then simplest solution is to define the custom classes in a separate file (module) and import it into your main file. Something like this:
Make a new file in same folder named custom_transformers.py with contents:
from sklearn.base import TransformerMixin, BaseEstimator
# Changed the base classes here, see Point 3
class PassthroughTransformer(BaseEstimator, TransformerMixin):
# I corrected the `fit()` method here, it should take X, y as input
def fit(self, X, y=None):
return self
def transform(self, X):
self.X = X
return X
# I have corrected the output here, See point 2
def get_feature_names(self):
return self.X.columns.tolist()
Now in your main file, do this:
from custom_transformers import PassthroughTransformer
For more information, see these questions:
- Python multiprocessing pickling error
- Python: Can't pickle type X, attribute lookup failed
Custom sklearn pipeline transformer giving "pickle.PicklingError" (I am suggesting this workaround)
You return
self.X.values.tolist():-
Here
Xis a PandasDataFrame, soX.values.tolist()will return the actual data of the columns you specify, not the column names. So even if you solve the first error, you will get error in this. Correct this to:
return self.X.columns.tolist()
(Minor) Class inheriting:
You defined the PassthroughTransformer as:
PassthroughTransformer(FunctionTransformer, BaseEstimator)
FunctionTransformeralready inherits fromBaseEstimatorso I dont think there is need to inherit fromBaseEstimator. You can change it in following ways:
class PassthroughTransformer(FunctionTransformer):
OR
# Standard way
class PassthroughTransformer(BaseEstimator, TransformerMixin):
Hope this helps.
There are multiple issues here:
Cannot clone object error is due to parallel processing:
By default,
scikit-learnclones (which usespickle) the supplied transformers and estimators when its working in Pipeline and similar (FeatureUnion, ColumnTransformer etc) or in cross-validation (cross_val_score,GridSearchCVetc).
Now, you have specified
n_jobs=-1in yourColumnTransformer, which introduces multiprocessing in the code. Python's inbuilt pickling dont work well with multiprocessing. And hence the error.
Options:
Set
n_jobs = 1to not use multiprocessing. Still need to correct the code according to point 2 and 3.If you want to use multiprocessing, then simplest solution is to define the custom classes in a separate file (module) and import it into your main file. Something like this:
Make a new file in same folder named custom_transformers.py with contents:
from sklearn.base import TransformerMixin, BaseEstimator
# Changed the base classes here, see Point 3
class PassthroughTransformer(BaseEstimator, TransformerMixin):
# I corrected the `fit()` method here, it should take X, y as input
def fit(self, X, y=None):
return self
def transform(self, X):
self.X = X
return X
# I have corrected the output here, See point 2
def get_feature_names(self):
return self.X.columns.tolist()
Now in your main file, do this:
from custom_transformers import PassthroughTransformer
For more information, see these questions:
- Python multiprocessing pickling error
- Python: Can't pickle type X, attribute lookup failed
Custom sklearn pipeline transformer giving "pickle.PicklingError" (I am suggesting this workaround)
You return
self.X.values.tolist():-
Here
Xis a PandasDataFrame, soX.values.tolist()will return the actual data of the columns you specify, not the column names. So even if you solve the first error, you will get error in this. Correct this to:
return self.X.columns.tolist()
(Minor) Class inheriting:
You defined the PassthroughTransformer as:
PassthroughTransformer(FunctionTransformer, BaseEstimator)
FunctionTransformeralready inherits fromBaseEstimatorso I dont think there is need to inherit fromBaseEstimator. You can change it in following ways:
class PassthroughTransformer(FunctionTransformer):
OR
# Standard way
class PassthroughTransformer(BaseEstimator, TransformerMixin):
Hope this helps.
answered Nov 20 at 7:51
Vivek Kumar
15k41851
15k41851
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53382322%2fadding-get-feature-names-to-columntransformer-pipeline%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown