0

I need to create messages of the kind Hi John, it's time to eat quantity_1 of food1, quantity_2 of food_2 ..., qunatity_n of food_n. For that I get pandas dataframes, that update every once in a while. For example the dataframes look sometimes like df1=

qantity,food
1,apple

and sometimes like df2=

quantity,food
1,apple
3,salads
2,carrots

I need with every new update to create out of the dataframe a string for a message. For df1 an f-string works neetly and for creating my desired message of Hi John, it's time to eat 1 apple. I can do:

f"Hi John, it's time to eat {df.quantity} {df1.food}

In the cases when I have something like df2, and I don't know explicitly how long df2 is, I would like to have a message of the kind Hi John, it's time to eat 1 apple, 3 salads, 2 carrots.

How can I create such a string? I thought of using the "splat" operator for something like join(*zip(df.quantity, df.food)), but I haven't figured it out. tnx

1
  • you need to convert df2 into dict then loop through keys and values.
    – user13824946
    Commented Oct 1, 2020 at 17:40

6 Answers 6

2

Try this:

result=','.join([str(i[0])+' '+i[1] for i in zip(df.quantity, df.food)])

print(result)

'1 apple, 2 salads, 3 carrots'

And you can add this to get the final result:

"Hi John, it's time to eat " + result

Hi John, it's time to eat 1 apple, 2 salads, 3 carrots
5
  • 1
    Looks even nicer with an f-string: result=','.join([f'{i[0]} {i[1]}' for i in zip(df.quantity, df.food)]) or result = ', '.join(f'{quantity} {food}' for quantity, food in zip(df.quantity, df.food))
    – Matthias
    Commented Oct 1, 2020 at 18:14
  • I like the brevity of the suggested solution, it is easy to read and see what happens, so I accept it as a solution!
    – NeStack
    Commented Oct 1, 2020 at 21:06
  • actually I tested it just now and I get the error message TypeError: zip argument #1 must support iteration. DO you know how to fix it?
    – NeStack
    Commented Oct 2, 2020 at 9:39
  • Strange. What is the length of your df? Maybe zip wouldn't work with len=1. Can you post first 3 rows of your df?
    – IoaTzimas
    Commented Oct 2, 2020 at 9:46
  • 1
    Sorry, I figured out the problem - I used the df from Siva's answer: df1 = pd.DataFrame({'size':['1','2'], 'Food':['apple', 'banana']}). The error came from the unfortunate column name size, because df.size was not treated as one of the columns, but as a method. Sorry again, it works in the general case
    – NeStack
    Commented Oct 2, 2020 at 10:27
1
import pandas as pd 
df = pd.DataFrame([[1, 'apple'], [2, 'salads'], [5, 'carrots']], columns=['quantity','food'])
menu =  ', '.join(f"{quantity} {food}" for idx, (quantity, food) in df.iterrows())
print(f"Hi John, it's time to eat {menu}.")

output

Hi John, it's time to eat 1 apple, 2 salads, 5 carrots.

using package inflect you can do it with better grammar:

import inflect

p=inflect.engine()
menu = p.join([f"{quantity} {food}" for idx, (quantity, food) in df.iterrows()])
print(f"Hi John, it's time to eat {menu}.")

output:

Hi John, it's time to eat 1 apple, 2 salads, and 5 carrots.

inflect even can construct correct singular/plural form

6
  • I like the iterrows use!
    – NeStack
    Commented Oct 1, 2020 at 21:05
  • I edited the answer from idx, (qunaity, food) in df.iterrows() to idx, row in df.iterrows(), so that the code works in a more general case, than the one with my dummy df2 where I have only 2 columns
    – NeStack
    Commented Oct 2, 2020 at 10:31
  • please, don't edit my code. variable names used are descriptive, as per your question - quantity and food. it's not the row (i.e. row suggests all values in the respective row). I revert your edit.
    – buran
    Commented Oct 2, 2020 at 10:52
  • I don't know about the programming jargon of descriptive and respective, but I can tell you that the edits that I made serve the intention of my task better. And the intention is why I have written the question in the first place. The dataframe df2 I have posted, and you refer to, is just a dummy. For my real task I logically use a df with many more columns and your code doesn't work with this real df, while my edits work with it. Furthermore, my edits would also suit the needs of future readers, that find this post, and don't have a df with just 2 columns. Поздрави :)
    – NeStack
    Commented Oct 2, 2020 at 11:23
  • Variable names that I use are consistent with your question. We all understand that it's a dummy DataFrame. You show dummy DataFrame with first column quantity and second column food and that's why I use these names. We don't care how you will adapt the solution for your real data.
    – buran
    Commented Oct 2, 2020 at 11:30
1

There are two ways to approach this. The first option is to create a message column in the dataframe

df = pd.DataFrame(data={'quantity':  [1],'food': ['apple']})
df['message'] = df.apply(lambda x: f"Hi John, it's time to eat {x.quantity} {x.food}", axis = 1)
print(df['message'])

the second option is to get slice the dataframe object by index to create your messages outside of the dataframe

f"Hi John, it's time to eat {df.quantity[0]} {df.food[0]}"

to handle multiple records in the dataframe, you can iterate through the rows

"Hi John, it's time to eat " + ", ".join(list((f"{df.quantity[i]} {df.food[i]}" for i in df.index)))
2
  • Thanks, but this only works for the case of df1, for the case of df2 with more than 1 row your suggestion doesn't deliver the desired output. Any suggestions for improvement?
    – NeStack
    Commented Oct 1, 2020 at 17:51
  • 1
    for mulitle records, you can use this "Hi John, it's time to eat " + ", ".join(list((f"{df.quantity[i]} {df.food[i]}" for i in df.index)))
    – Concorde
    Commented Oct 1, 2020 at 17:52
1

I prefer to use str.format() for complex scenarios as it makes the code easier to read.

In this case:

import pandas as pd
df2=pd.DataFrame({'quantity':[1,3,2],'food':['apple','salads','carrots']})
def create_advice(df):
    s="Hi John, it's time to eat "+", ".join(['{} {}'.format(row['quantity'],row['food']) for index,row in df.iterrows()])+'.'
    return s

create_advice(df2)
>"Hi John, it's time to eat 1 apple, 3 salads, 2 carrots."

You might also want to modify the df slightly before creating the strings:

list_of_portioned_food=['salads']
df2['food']=df2.apply(lambda row: ('portions of ' if row['food'] in list_of_portioned_food else '')+row['food'],axis=1)
df2.iloc[len(df2)-1,1]='and '+str(df2.iloc[len(df2)-1,1])

applying the above function again:

create_advice(df2)
> "Hi John, it's time to eat 1 apple, 3 portions of salads, and 2 carrots."
1

Try this

df1 = pd.DataFrame({'size':['1','2'], 'Food':['apple', 'banana']})
l_1 = [x + '' + y for x, y in zip(df1['size'], df1['Food'])]
"Hi John, it's time to eat " + ", ".join(l_1)
3
  • Thanks, what I get is Hi John, it's time to eat 1 apple,2 banana,3 mango. Do you know how to add space after the comas?
    – NeStack
    Commented Oct 2, 2020 at 9:41
  • 1
    a space after "," like ", " would do. New to Pandas ? Commented Oct 2, 2020 at 10:02
  • Sorry, I could have used a little more imagination :)
    – NeStack
    Commented Oct 2, 2020 at 10:19
0

I actually used SQLite3 for my project, but the solution still holds: You can create a concatenating function that loops through the args, appends them to a string, and insert that resulting complete string into another f string.

def concat_String(values):
    str = ""
    for v in values:
        str += f" {v}"
    return str

def create_New_Table(con, cur, table_name, values):
    try:
        cur.execute(f"CREATE TABLE {table_name}({concat_String(values)})")

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.