Partial string matching on large data set - python
So I have been working on this for a while and just haven't got any where and just not sure what to do.Fairly new to pandas and python.
Data set is actually 15,000 product names. All in different formats, some have multiple dashes up to 6, some hyphens, different lengths,The rows are product names with variants.
The code i'm using keeps returning only the first letter as oppose to the partial string when I use it on a large data set.
Works just fine on a small data set which I was using to test it.
I'm assuming this is happening because:
I haven't created a stop section when it matches a full partial string
because its trying to match up words as oppose to individual characters and stopping when it finds a difference.
What is the best way to overcome this on a large data set, what am I missing? or am I going to have to do this manual?
Original test data set
`1.star t-shirt-large-red
2.star t-shirt-large-blue
3.star t-shirt-small-red
4.beautiful rainbow skirt small
5.long maxwell logan jeans- light blue -32L-28W
6.long maxwell logan jeans- Dark blue -32L-28W`
Desired data set/output:
`COL1 COL2 COL3 COL4
1[star t-shirt] [large] [red] NONE
2[star t-shirt] [large] [blue] NONE
3[star t-shirt] [small] [red] NONE
4[beautiful rainbow skirt small] [small] NONE NONE
5[long maxwell logan jeans] [light blue] [32L] [28W]
6[long maxwell logan jeans] [Dark blue] [32L] [28W]`
Here is the code I was helped with in a previous question I asked:
`df['onkey'] = 1
df1 = pd.merge(df[['name','onkey']],df[['name','onkey']], on='onkey')
df1['list'] = df1.apply(lambda x:[x.name_x,x.name_y],axis=1)
from os.path import commonprefix
df1['COL1'] = df1['list'].apply(lambda x:commonprefix(x))
df1['COL1_num'] = df1['COL1'].apply(lambda x:len(x))
df1 = df1[(df1['COL1_num']!=0)]
df1 = df1.loc[df1.groupby('name_x')['COL1_num'].idxmin()]
df = df.rename(columns ={'name':'name_x'})
df = pd.merge(df,df1[['name_x','COL1']],on='name_x',how ='left')`
`df['len'] = df['COL1'].apply(lambda x: len(x))
df['other'] = df.apply(lambda x: x.name_x[x.len:],axis=1)
df['COL1'] = df['COL1'].apply(lambda x: x.strip())
df['COL1'] = df['COL1'].apply(lambda x: x[:-1] if x[-1]=='-' else x)
df['other'] = df['other'].apply(lambda x:x.split('-'))
df = df[['COL1','other']]
df = pd.concat([df['COL1'],df['other'].apply(pd.Series)],axis=1)`
` COL1 0 1 2
0 star t-shirt large red NaN
1 star t-shirt large blue NaN
2 star t-shirt small red NaN
3 beautiful rainbow skirt small NaN NaN
4 long maxwell logan jeans light blue 32L 28W
5 long maxwell logan jeans Dark blue 32L 28W`
***************update*****************
This is your input list of product,some have variants and some don't.
When searching for duplicates strings to determine what are the products with variants and products without variants;nothing comes up because they are all seen as unique values due to the variants being added on at the end of the string.
So what I would like to do is group the partial or similar strings together(the longest match), extract the longest matching string within the group and then put the differences into other columns.
If the product /string is unique just print into the column with the extracted longest string.
star t-shirt-large-red
star t-shirt-large-blue
star t-shirt-small-red
beautiful rainbow skirt small
long maxwell logan jeans- light blue -32L-28W
long maxwell logan jeans- Dark blue -32L-28W
Organic and natural candy - 3 Pack - Mint
Organic and natural candy - 3 Pack - Vanilla
Organic and natural candy - 3 Pack - Strawberry
Organic and natural candy - 3 Pack - Chocolate
Organic and natural candy - 3 Pack - Banana
Organic and natural candy - 3 Pack - Cola
Organic and natural candy - 12 Pack Assorted
Morgan T-shirt Company - Small/Medium-Blue
Morgan T-shirt Company - Medium/Large-Blue
Morgan T-shirt Company - Medium/Large-red
Morgan T-shirt Company - Small/Medium-Red
Morgan T-shirt Company - Small/Medium-Green
Morgan T-shirt Company - Medium/Large-Green
Nelly dress leopard small
col1 col2 col3 col4
star t-shirt large red
star t-shirt large blue
star t-shirt small red
beautiful rainbow skirt small
Long maxwell logan jeans light blue 32L 28W
Long maxwell logan jeans Dark blue 32L 28W
Organic and natural candy 3 Pack Mint
Organic and natural candy 3 Pack Vanilla
Organic and natural candy 3 Pack Strawberry
Organic and natural candy 3 Pack Chocolate
Organic and natural candy 3 Pack Banana
Organic and natural candy 3 Pack Cola
Organic and natural candy 12 Pack Assorted
Morgan T-shirt Company Small/Medium Blue
Morgan T-shirt Company Medium/Large Blue
Morgan T-shirt Company Medium/Large Red
Morgan T-shirt Company Small/Medium Red
Morgan T-shirt Company Small/Medium Green
Morgan T-shirt Company Medium/Large Green
Nelly dress Leopard Small
Bijoux
Princess PJ-set
Lemon tank top Yellow Medium
Constructing a DataFrame df as follows:
df = pd.DataFrame()
df = df.append(['1.star t-shirt-large-red'])
df = df.append(['2.star t-shirt-large-blue'])
df = df.append(['4.beautiful rainbow skirt small'])
df = df.append(['5.long maxwell logan jeans- light blue -32L-28W'])
df = df.append(['6.long maxwell logan jeans- Dark blue -32L-28W'])
df.columns = ['Product']
The following code
(a) strips any whitespace,
(b) splits by the period ('.') and grabs what follows,
(c) replaces 't-shirt' with 'tshirt' because of further operations (change this back if you want after the operation)
(d) splits again by '-' and expands to give your dataframe.
df['Product'].str.strip().str.split('.').str.get(1).str.replace('t-shirt', 'tshirt').str.split('-', expand = True)
Output:
0 1 2 3
0 star tshirt large red None
0 star tshirt large blue None
0 beautiful rainbow skirt small None None None
0 long maxwell logan jeans light blue 32L 28W
0 long maxwell logan jeans Dark blue 32L 28W
Given the inconsistency in nomenclature for your product, there will be edge-cases that are missed (ex : beautiful rainbow skirt small). You may have to fish them out again.
A solution which is quite simple to understand, debug and flexibly extend is the following:
Consider that your initial product names are held in a list called strings.
Then the solution is the following line:
mydf = pd.concat([pd.DataFrame([make_row(row, 4)], columns=['COL1', 'COL2', 'COL3', 'COL4']) for row in strings], ignore_index=True)
where we have defined the parsing function make_row to be:
def make_row(string, num_cols):
cols = [item.strip() for item in string[2:].split('-')] # ignore numbering, split on hyphen and strip whitespace
if len(cols) < num_cols:
cols += [np.nan]*(num_cols - len(cols)) # fill with NaN missing values
return cols
The first line defining cols could also be simply cols = string.split('-'), in which case you could do the formatting afterwards with:
mydf.applymap(lambda x: x if pd.isnull(x) else str.strip(x))
Now in your case, I see that there is a hyphen in some of your product names, in which case you might want to 'sanitize' them in advance (or inside make_row, as you wish), with something like:
strings = [item.replace('t-shirt', 'tshirt') for item in strings]
Example input:
strings = ['1.one-two-three', '2. one-two', '3.one-two-three-four', '4.one - two -three -four ']
Output:
COL1 COL2 COL3 COL4
0 one two three NaN
1 one two NaN NaN
2 one two three four
3 one two three four
Output for question's data (after correcting typo for item 4):
COL1 COL2 COL3 COL4
0 star tshirt large red NaN
1 star tshirt large blue NaN
2 star tshirt small red NaN
3 beautiful rainbow skirt small NaN NaN
4 long maxwell logan jeans light blue 32L 28W
5 long maxwell logan jeans Dark blue 32L 28W
Edit:
If you additionally want to "group" the items together, then you can:
a) Use sort_values (pandas doc) on the column COL1 after you get a dataframe as described above to simply display the rows corresponding to the same product the one after the other, or
b) use group_by to actually get a grouped dataframe like this:
grouped_df = mydf.groupby("COL1")
This will allow you to get each group like this:
grouped_df.get_group("star tshirt")
Producing following output:
COL1 COL2 COL3 COL4
0 star tshirt large red NaN
1 star tshirt large blue NaN
2 star tshirt small red NaN
Related
Extraction of a common column pandas
I have two data frames, I need to extract data in Column_3 of the second dataframe DF2. Question 1: How should I create "Column_3" from "Column_1" and "Column_2" of the first dataframe? DF1 = Column_1 Column_2 Column_3 Red Apple small Red Apple small Green fruit Large Green fruit Large Yellow Banana Medium Yellow Banana Medium Pink Mango Tiny Pink Mango Tiny Question 2: I need to extract "n_col3" from n_col1 & n_col2 but that should be similar to the column_3 of data frame 1. (see the brackets for info of what to be extracted) Note: If all the information of Column_3 is not available in Column_1 & Column_2 like in Row 1 & Row 3, Only that information that is available should be extracted) DF2 = n_col1 n_col2 n_col3 L854 fruit Charlie Green LTD Large fruit Large(Green missing Fruit Large extracted) Red alpha 8 small Tango G250 Apple Red Apple small(all information extracted) Mk43 Mango Beta Tiny J448 T Mango Tiny(Pink missing, Mango Tiny is extracted) M40 Yellow Medium Romeo Banana Yellow Banana Medium(all information extracted) I want to extract that column so that I can do further processing of merging. Can anyone help me with this. Thank you in advance.
Splitting a column into two in dataframe
It's solution is definitely out there but I couldn't find it. So posting it here. I have a dataframe which is like object_Id object_detail 0 obj00 red mug 1 obj01 red bowl 2 obj02 green mug 3 obj03 white candle holder I want to split the column object_details into two columns: name, object_color based on a list that contains the color name COLOR = ['red', 'green', 'blue', 'white'] print(df) # want to perform some operation so that It'll get output object_Id object_detail object_color name 0 obj00 red mug red mug 1 obj01 red bowl red bowl 2 obj02 green mug green mug 3 obj03 white candle holder white candle holder This is my first time using dataframe so I am not sure how to achieve it using pandas. I can achieve it by converting it into a list and then apply a filter. But I think there are easier ways out there that I might miss. Thanks
Use Series.str.extract with joined values of list by | for regex OR and then all another values in new column splitted by space: pat = "|".join(COLOR) df[['object_color','name']] = df['object_detail'].str.extract(f'({pat})\s+(.*)',expand=True) print (df) object_Id object_detail object_color name 0 obj00 Barbie Pink frock Barbie Pink frock 1 obj01 red bowl red bowl 2 obj02 green mug green mug 3 obj03 white candle holder white candle holder
Python Pandas dataframe cross-referencing and new column generation
I want to generate a dataframe that contains lists of a person's potential favorite crayon colors, based on their favorite color. I have two dataframes that contain the necessary information: df1 = pd.DataFrame({'person':['Jeff','Marie','Jenna','Mike'], 'color':['blue', 'purple', 'brown', 'green']}, columns=['person','color']) df2 = pd.DataFrame({'possible_crayons':['christmas red','infra red','scarlet','sunset orange', 'neon carrot','lemon','forest green','pine','navy','aqua','periwinkle','royal purple'],'color':['red','red','red','orange','orange','yellow','green','green','blue','blue','purple','purple']}, columns=['possible_crayons','color']) I want to reference one database against the other by matching the df1 color entry to the df2 color entry, and returning the corresponding possible_crayons values as a list in a new column in df1. Any terms that did not find a match would be labeled N/A. So the desired output would be: person favorite_color possible_crayons_list Jeff blue [navy, aqua] Marie purple [periwinkle, royal purple] Jenna brown NaN Mike green [forest green, pink] I've tried: mergedDF = pd.merge(df1, df2, how='left') However, this results in the following: person color possible_crayons 0 Jeff blue navy 1 Jeff blue aqua 2 Marie purple periwinkle 3 Marie purple royal purple 4 Jenna brown NaN 5 Mike green forest green 6 Mike green pine Is there any way to achieve my desired output of lists?
We can use DataFrame.merge with how='left' and then GroupBy.agg with as_index=False: new_df= ( df1.merge(df2,how='left',on='color') .groupby(['color','person'],as_index=False).agg(list) ) Output print(new_df) color person possible_crayons 0 blue Jeff [navy, aqua] 1 brown Jenna [nan] 2 green Mike [forest green, pine] 3 purple Marie [periwinkle, royal purple]
Use this: df1 = pd.DataFrame({'person':['Jeff','Marie','Jenna','Mike'], 'color':['blue', 'purple', 'brown', 'green']}, columns=['person','color']) df2 = pd.DataFrame({'possible_crayons':['christmas red','infra red','scarlet','sunset orange', 'neon carrot','lemon','forest green','pine','navy','aqua','periwinkle','royal purple'],'color':['red','red','red','orange','orange','yellow','green','green','blue','blue','purple','purple']}, columns=['possible_crayons','color']) tmp = df2.groupby('color')['possible_crayons'].apply(list) mergedDF = df1.merge(tmp, how='left', left_on='color', right_index=True) print(mergedDF)
mergedDF2 = mergedDF.groupby('color')['possible_crayons'].apply(list).reset_index(name='new_possible_crayons')
Find phrases stored in a dataframe in sentences found in another dataframe
assuming I have 2 dataframes: sub = pd.DataFrame(['Little Red', 'Grow Your', 'James Bond', 'Tom Brady']) text = pd.DataFrame(['Little Red Corvette must Grow Your ego', 'Grow Your Beans', 'James Dean and his Little Red coat', 'I love pasta']) One containing various subjects and the other text from where I should be able to extract the subjects I want the output of text dataframe to be: Text | Subjects Little Red Corvette must Grow Your ego | Little Red, Grow Your Grow Your Beans | Grow Your James Dean and his Little Red coat | Little Red I love pasta | NaN Any idea how I can achieve this? I was looking at this question: Check if words in one dataframe appear in another (python 3, pandas) but it is not exactly as my desired output. Thankyou
Use str.findall with joined all values of sub by | with regex word boundary: pat = '|'.join(r"\b{}\b".format(x) for x in sub[0]) text['new'] = text[0].str.findall(pat).str.join(', ') print (text) 0 new 0 Little Red Corvette must Grow Your ego Little Red, Grow Your 1 Grow Your Beans Grow Your 2 James Dean and his Little Red coat Little Red 3 I love pasta If want NaN for not matched values use loc: pat = '|'.join(r"\b{}\b".format(x) for x in sub[0]) lists = text[0].str.findall(pat) m = lists.astype(bool) text.loc[m, 'new'] = lists.loc[m].str.join(',') print (text) 0 new 0 Little Red Corvette must Grow Your ego Little Red,Grow Your 1 Grow Your Beans Grow Your 2 James Dean and his Little Red coat Little Red 3 I love pasta NaN
Merging 2 dataframe using similar columns
I have 2 dataframe listed as follow df Type Breed Common Color Other Color Behaviour Golden Big Gold White Fun Corgi Small Brown White Crazy Bulldog Medium Black Grey Strong df2 Type Breed Behaviour Bark Sound Pug Small Sleepy Ak German Shepard Big Cool Woof Puddle Small Aggressive Ek I wanted to merge 2 dataframe by columns Type, Breed and Behavior. Therefore, my desire output would be: Type Breed Behavior Golden Big Fun Corgi Small Crazy Bulldog Medium Strong Pug Small Sleepy German Shepard Big Cool Puddle Small Aggressive
You need concat: print (pd.concat([df1[['Type','Breed','Behaviour']], df2[['Type','Breed','Behaviour']]], ignore_index=True)) Type Breed Behaviour 0 Golden Big Fun 1 Corgi Small Crazy 2 Bulldog Medium Strong 3 Pug Small Sleepy 4 German Shepard Big Cool 5 Puddle Small Aggressive More general is use intersection for columns of both DataFrames: cols = df1.columns.intersection(df2.columns) print (cols) Index(['Type', 'Breed', 'Behaviour'], dtype='object') print (pd.concat([df1[cols], df2[cols]], ignore_index=True)) Type Breed Behaviour 0 Golden Big Fun 1 Corgi Small Crazy 2 Bulldog Medium Strong 3 Pug Small Sleepy 4 German Shepard Big Cool 5 Puddle Small Aggressive More general if df1 and df2 have no NaN values use dropna for removing columns with NaN: print (pd.concat([df1 ,df2], ignore_index=True)) Bark Sound Behaviour Breed Common Color Other Color Type 0 NaN Fun Big Gold White Golden 1 NaN Crazy Small Brown White Corgi 2 NaN Strong Medium Black Grey Bulldog 3 Ak Sleepy Small NaN NaN Pug 4 Woof Cool Big NaN NaN German Shepard 5 Ek Aggressive Small NaN NaN Puddle print (pd.concat([df1 ,df2], ignore_index=True).dropna(1)) Behaviour Breed Type 0 Fun Big Golden 1 Crazy Small Corgi 2 Strong Medium Bulldog 3 Sleepy Small Pug 4 Cool Big German Shepard 5 Aggressive Small Puddle
using join dropping columns that don't overlap df1.T.join(df2.T, lsuffix='_').dropna().T.reset_index(drop=True)