How to do these extraction/computations efficiently? I have a set of products(e.g. P0006, P0009, etc.) and a set of production lines (eg. L1H175, L2H175). A product can be produced on a subset of the lines set (and not necessarily all of them).

If we decide to produce a product on a production line(assuming that the line can actually produce it), we need to buy the appropriate raw materials (e.g. If we decide to produce P0009 on L1h175 we need to buy the set of the selected raw materials in the image). Each raw material has a price and a property(S or NS). 

What I want the code to do: 


For each product, return where it can be produced. For example, to see where P0009 can be produced, I need to look at all the rows of the fist columns and whenever I find it, I add the appropriate line id to the set of lines that produce that product.  
Reversely, for each line, what are the products that it can produce.
For each (p,l) couple store the set of all need raw materials, to produce product p on l, the property of each raw material(coded as 1 if it's S and 0 otherwise) and the total price (I don't need individual prices).


Here is how I am doing it now: 

def process(x, y, recipes_df):
    raw_materials = {}
    total_cost = 0
    for product, line, rm, rm_type, rm_price in \
    recipes_df[['product','line', 'rm','rm_type','rm_price']].values:
        if (x,y) == (product,line):
            total_cost += float(rm_price)
            raw_materials[rm] = 1 if rm_type == 'S' else 0
    return (raw_materials, total_cost)

lines_set = set(recipes_df['line'])
lines = []

rm_data = {}
products_set = set()
for line in lines_set:
    for row in recipes_df.itertuples(index=False):
        # if the line_id in this row correspond to line in the outer loop
        if row[1] == line:
            # extract the raw material information by using the process function.
            rm_data[row[0]] = process(row[0],row[1], recipes_df)

            # add the product to the set of products that can be manufactured on the line
            products_set.add(row[0])

            # add the informations to lines list
            lines.append((rm_data, products_set))


The file has more than 3000 lines, my approach takes a lot of time.

Observations:
0. There is no assumption about how the file is sorted, meaning that, I can find P0009 in line 1 and in line 3000, for example, with a lot of other products in between. Same remark for the lines.
1. The process function search in all the file every time it's cold for a couple (p,l), which may be inefficient
2. For every line in lines_set we browse all the file. 



Question: How to do all of this more efficiently?

Edit : I did only 2. and 3., the 1. is very similar to 2.

Edit2: recipes_df is a pandas data frame

Edit3:  The link to the data
In general if you have relational data in a list-like data structure, you want to convert it into a more appropriate data structure before you do anything tricky with it.  I'm not sure what the type of recipes_df so I'm just going to copy your code that iterates over it and go from there.

from collections import defaultdict
from typing import Dict, List, Set, Tuple

# For each product, return where it can be produced.
product_lines: Dict[str, Set[str]] = defaultdict(set)

# For each line, what are the products that it can produce.
line_products: Dict[str, Set[str]] = defaultdict(set)

# For each (p,l) couple store the set of all need raw materials 
# to produce product p on l, the property of each raw material
# (coded as 1 if it's S and 0 otherwise), and the total price
class Recipe:
    def __init__(self):
        self.raw_materials: List[Tuple[str, int]] = []
        self.total_price = 0.0
recipes: Dict[Tuple[str, str], Recipe] = defaultdict(Recipe)

# Loop over recipes_df and populate all these structures.
for product, line, rm, rm_type, rm_price in \
        recipes_df[['product','line', 'rm','rm_type','rm_price']].values:
    product_lines[product].add(line)
    line_products[line].add(product)
    recipes[(product, line)].raw_materials.append((rm, 1 if rm_type == 'S' else 0))
    recipes[(product, line)].total_price += rm_price


At this point you've done a single pass through the entirety of recipes_df and you've completely populated the dictionaries that will let you find all the information you're looking for in constant time.  From here you can convert those dictionaries into whatever other output format you need.