<h3 dir="auto">Describe the workflow you want to enable</h3>
<p dir="auto">To the best of my understanding, sklearn pipeline cache helps reduce computation when the full argument list matches what has been cached. The main motivation of caching seems to cache the transformer steps, and transformer often only depends on X, not y. However, the current caching considers both X and y together to decide whether to compute again.</p>
<p dir="auto">For example, the transformer could transform both X and y separately as a function of some parameter to be searched over using GridSearchCV. Repeated transformation of X for different y could be very expensive.</p>
<p dir="auto">Another scenario is that there are different ways to construct X given some parameter to be searched over using GridSearchCV. A pipeline can have a transformer to achieve this as the first step. During pipeline.fit(), the transformer.fit() will ignore X and y and create actual data, and transformer.transform(X) can accept X as a row index to select a subset from the created data as output to be used in the following steps of the pipeline.</p>
<p dir="auto">In the following code, it shows that pipeline.fit() is computed for all the input pairs, because y is different. (It also seems that currently the internal state of the pipeline will decide whether to compute again or not) It will be great if there could be an ignore argument when initializing Pipeline, which can allow the reuse of some previous transformer fit (and possibly the option to ignore certain internal states/attributes if necessary).</p>
<p dir="auto">I have searched for solutions, and tried different ideas. For example, if the cache can apply to certain steps of a pipeline, that would serve the purpose as well, but I didn't find such an option. I thought of having a pipeline inside another pipeline, and cache the inside one, but the input argument from the outer to the inner pipeline will give rise to the same issue.</p>
<p dir="auto">Maybe there is already a way to allow flexible data creation given a parameter to be searched over in GridSearchCV that I do not know of. If so, I would appreciate it if you point me to the reference. Thank you very much!</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="class TestTransformer(BaseEstimator, TransformerMixin):
    def __init__(self, label: str, fit_counter: int = 0, transform_counter: int = 0):
        self.label = label
        self.fit_counter = fit_counter
        self.transform_counter = transform_counter

    def fit(self, X, y=None):
        print('fit', self.label)
        self.fit_counter += 1
        return self

    def transform(self, X, y=None):
        # &quot;expensive&quot; transformation
        print('transform', self.label)
        self.transform_counter += 1
        return X

## each call of pipeline.fit() will be computed, because X and y as a pair are different
cachedir = mkdtemp()
memory = joblib.Memory(location=cachedir, verbose=1)

pipeline = Pipeline(
    [
        (&quot;test1&quot;, TestTransformer(label=&quot;1&quot;)),
        (&quot;test2&quot;, TestTransformer(label=&quot;2&quot;))
    ],
    memory=memory,
)

pipeline.fit(X=1, y=1)
pipeline.fit(X=1, y=2)
pipeline.fit(X=1, y=3)
print('test1:',
      'fit', pipeline.named_steps['test1'].fit_counter,
      'transform', pipeline.named_steps['test1'].transform_counter)
print('test2:',
      'fit', pipeline.named_steps['test2'].fit_counter,
      'transform', pipeline.named_steps['test2'].transform_counter)"><pre class="notranslate"><code class="notranslate">class TestTransformer(BaseEstimator, TransformerMixin):
    def __init__(self, label: str, fit_counter: int = 0, transform_counter: int = 0):
        self.label = label
        self.fit_counter = fit_counter
        self.transform_counter = transform_counter

    def fit(self, X, y=None):
        print('fit', self.label)
        self.fit_counter += 1
        return self

    def transform(self, X, y=None):
        # "expensive" transformation
        print('transform', self.label)
        self.transform_counter += 1
        return X

## each call of pipeline.fit() will be computed, because X and y as a pair are different
cachedir = mkdtemp()
memory = joblib.Memory(location=cachedir, verbose=1)

pipeline = Pipeline(
    [
        ("test1", TestTransformer(label="1")),
        ("test2", TestTransformer(label="2"))
    ],
    memory=memory,
)

pipeline.fit(X=1, y=1)
pipeline.fit(X=1, y=2)
pipeline.fit(X=1, y=3)
print('test1:',
      'fit', pipeline.named_steps['test1'].fit_counter,
      'transform', pipeline.named_steps['test1'].transform_counter)
print('test2:',
      'fit', pipeline.named_steps['test2'].fit_counter,
      'transform', pipeline.named_steps['test2'].transform_counter)
</code></pre></div>
<h3 dir="auto">Describe your proposed solution</h3>
<p dir="auto">It seems that joblib.Memory.cache has an ignore argument, which allows ignoring certain arguments in decorating the function being cached. If we could allow the user to specify which arguments in the pipeline.fit() when creating the pipeline, that would make pipeline more flexible.</p>
<h3 dir="auto">Describe alternatives you've considered, if relevant</h3>
<p dir="auto"><em>No response</em></p>
<h3 dir="auto">Additional context</h3>
<p dir="auto"><em>No response</em></p>
<h3 dir="auto">Describe the workflow you want to enable</h3>
<p dir="auto">To the best of my understanding, sklearn pipeline cache helps reduce computation when the full argument list matches what has been cached. The main motivation of caching seems to cache the transformer steps, and transformer often only depends on X, not y. However, the current caching considers both X and y together to decide whether to compute again.</p>
<p dir="auto">For example, the transformer could transform both X and y separately as a function of some parameter to be searched over using GridSearchCV. Repeated transformation of X for different y could be very expensive.</p>
<p dir="auto">Another scenario is that there are different ways to construct X given some parameter to be searched over using GridSearchCV. A pipeline can have a transformer to achieve this as the first step. During pipeline.fit(), the transformer.fit() will ignore X and y and create actual data, and transformer.transform(X) can accept X as a row index to select a subset from the created data as output to be used in the following steps of the pipeline.</p>
<p dir="auto">In the following code, it shows that pipeline.fit() is computed for all the input pairs, because y is different. (It also seems that currently the internal state of the pipeline will decide whether to compute again or not) It will be great if there could be an ignore argument when initializing Pipeline, which can allow the reuse of some previous transformer fit (and possibly the option to ignore certain internal states/attributes if necessary).</p>
<p dir="auto">I have searched for solutions, and tried different ideas. For example, if the cache can apply to certain steps of a pipeline, that would serve the purpose as well, but I didn't find such an option. I thought of having a pipeline inside another pipeline, and cache the inside one, but the input argument from the outer to the inner pipeline will give rise to the same issue.</p>
<p dir="auto">Maybe there is already a way to allow flexible data creation given a parameter to be searched over in GridSearchCV that I do not know of. If so, I would appreciate it if you point me to the reference. Thank you very much!</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="class TestTransformer(BaseEstimator, TransformerMixin):
    def __init__(self, label: str, fit_counter: int = 0, transform_counter: int = 0):
        self.label = label
        self.fit_counter = fit_counter
        self.transform_counter = transform_counter

    def fit(self, X, y=None):
        print('fit', self.label)
        self.fit_counter += 1
        return self

    def transform(self, X, y=None):
        # &quot;expensive&quot; transformation
        print('transform', self.label)
        self.transform_counter += 1
        return X

## each call of pipeline.fit() will be computed, because X and y as a pair are different
cachedir = mkdtemp()
memory = joblib.Memory(location=cachedir, verbose=1)

pipeline = Pipeline(
    [
        (&quot;test1&quot;, TestTransformer(label=&quot;1&quot;)),
        (&quot;test2&quot;, TestTransformer(label=&quot;2&quot;))
    ],
    memory=memory,
)

pipeline.fit(X=1, y=1)
pipeline.fit(X=1, y=2)
pipeline.fit(X=1, y=3)
print('test1:',
      'fit', pipeline.named_steps['test1'].fit_counter,
      'transform', pipeline.named_steps['test1'].transform_counter)
print('test2:',
      'fit', pipeline.named_steps['test2'].fit_counter,
      'transform', pipeline.named_steps['test2'].transform_counter)"><pre class="notranslate"><code class="notranslate">class TestTransformer(BaseEstimator, TransformerMixin):
    def __init__(self, label: str, fit_counter: int = 0, transform_counter: int = 0):
        self.label = label
        self.fit_counter = fit_counter
        self.transform_counter = transform_counter

    def fit(self, X, y=None):
        print('fit', self.label)
        self.fit_counter += 1
        return self

    def transform(self, X, y=None):
        # "expensive" transformation
        print('transform', self.label)
        self.transform_counter += 1
        return X

## each call of pipeline.fit() will be computed, because X and y as a pair are different
cachedir = mkdtemp()
memory = joblib.Memory(location=cachedir, verbose=1)

pipeline = Pipeline(
    [
        ("test1", TestTransformer(label="1")),
        ("test2", TestTransformer(label="2"))
    ],
    memory=memory,
)

pipeline.fit(X=1, y=1)
pipeline.fit(X=1, y=2)
pipeline.fit(X=1, y=3)
print('test1:',
      'fit', pipeline.named_steps['test1'].fit_counter,
      'transform', pipeline.named_steps['test1'].transform_counter)
print('test2:',
      'fit', pipeline.named_steps['test2'].fit_counter,
      'transform', pipeline.named_steps['test2'].transform_counter)
</code></pre></div>
<h3 dir="auto">Describe your proposed solution</h3>
<p dir="auto">It seems that joblib.Memory.cache has an ignore argument, which allows ignoring certain arguments in decorating the function being cached. If we could allow the user to specify which arguments in the pipeline.fit() when creating the pipeline, that would make pipeline more flexible.</p>
<h3 dir="auto">Describe alternatives you've considered, if relevant</h3>
<p dir="auto"><em>No response</em></p>
<h3 dir="auto">Additional context</h3>
<p dir="auto"><em>No response</em></p>