পান্ডা প্রয়োগ থেকে একাধিক কলাম রিটার্ন করুন ()


113

আমার কাছে একটি পান্ডাস ডেটা ফ্রেম আছে df_test,। এটিতে একটি কলাম 'আকার' রয়েছে যা বাইটগুলিতে আকার উপস্থাপন করে। আমি নিম্নলিখিত কোড ব্যবহার করে কেবি, এমবি এবং জিবি গণনা করেছি:

df_test = pd.DataFrame([
    {'dir': '/Users/uname1', 'size': 994933},
    {'dir': '/Users/uname2', 'size': 109338711},
])

df_test['size_kb'] = df_test['size'].astype(int).apply(lambda x: locale.format("%.1f", x / 1024.0, grouping=True) + ' KB')
df_test['size_mb'] = df_test['size'].astype(int).apply(lambda x: locale.format("%.1f", x / 1024.0 ** 2, grouping=True) + ' MB')
df_test['size_gb'] = df_test['size'].astype(int).apply(lambda x: locale.format("%.1f", x / 1024.0 ** 3, grouping=True) + ' GB')

df_test


             dir       size       size_kb   size_mb size_gb
0  /Users/uname1     994933      971.6 KB    0.9 MB  0.0 GB
1  /Users/uname2  109338711  106,776.1 KB  104.3 MB  0.1 GB

[2 rows x 5 columns]

আমি এটি ১২০,০০০ এরও বেশি সারি এবং সময় চালিয়েছি এটি% টাইমিট অনুসারে কলাম * 3 = ~ 9 সেকেন্ডে প্রায় 2.97 সেকেন্ড সময় নেয়।

আমি কি আরও দ্রুত এটি করতে পারি? উদাহরণস্বরূপ, আমি কি একবারে একবারে একবারে কলামটি প্রয়োগ এবং এটি 3 বার চালানোর পরিবর্তে, মূল ডাটাফ্রেমে ফিরে প্রবেশের জন্য একটি পাসে সমস্ত তিনটি কলাম ফিরিয়ে দিতে পারি?

অন্য যে প্রশ্নগুলি আমি পেয়েছি সেগুলি একাধিক মান নিতে এবং একটি একক মান ফিরিয়ে দিতে চায় । আমি একটি একক মান নিতে এবং একাধিক কলাম ফিরে দিতে চাই

উত্তর:


131

এটি একটি পুরানো প্রশ্ন, তবে সম্পূর্ণতার জন্য, আপনি প্রয়োগ হওয়া ফাংশন থেকে একটি সিরিজ ফিরে আসতে পারেন যাতে নতুন তথ্য রয়েছে যা তিনবার পুনরাবৃত্তি করার প্রয়োজন প্রতিরোধ করে। axis=1প্রয়োগ ফাংশনে পাস করা sizesডেটাফ্রেমের প্রতিটি সারিটিতে ফাংশন প্রয়োগ করে , একটি নতুন ডেটাফ্রেমে যোগ করার জন্য একটি সিরিজ ফিরিয়ে দেয়। এই সিরিজ, গুলি, নতুন মান পাশাপাশি মূল তথ্য অন্তর্ভুক্ত করে।

def sizes(s):
    s['size_kb'] = locale.format("%.1f", s['size'] / 1024.0, grouping=True) + ' KB'
    s['size_mb'] = locale.format("%.1f", s['size'] / 1024.0 ** 2, grouping=True) + ' MB'
    s['size_gb'] = locale.format("%.1f", s['size'] / 1024.0 ** 3, grouping=True) + ' GB'
    return s

df_test = df_test.append(rows_list)
df_test = df_test.apply(sizes, axis=1)

12
আমি অবাক হয়েছি এটি সঠিক উত্তর ছাড়াই প্রায় 2 বছর অতিবাহিত করেছে। আমি অন্য কিছুর সন্ধান করছিলাম এবং এতে হোঁচট খেয়েছি। আশা করি কাজে লাগতে দেরি হয়নি!
নেলজ 11

11
কি rows_listএই উত্তরটি কি?
ডেভিড স্ট্যানসবি

এটি ডেটাফ্রেম তৈরির জন্য কেবল সিরিজের একটি তালিকা।
নেলজ 11

4
যদি পিডি.সারিজের একটি সূচকের প্রয়োজন হয় তবে আপনার এটি সরবরাহ করতে হবে pd.Series(data, index=...)। অন্যথায় আপনি যখন প্যারেন্ট ডেটাফ্রেমে ফলাফলটি পুনরায় নির্ধারণ করার চেষ্টা করবেন তখন আপনি ক্রিপ্টিক ত্রুটিগুলি পেয়ে যান।
smci

106

প্রয়োগ এবং জিপ ব্যবহার করুন সিরিজের উপায়ে 3 গুণ দ্রুত হবে।

def sizes(s):    
    return locale.format("%.1f", s / 1024.0, grouping=True) + ' KB', \
        locale.format("%.1f", s / 1024.0 ** 2, grouping=True) + ' MB', \
        locale.format("%.1f", s / 1024.0 ** 3, grouping=True) + ' GB'
df_test['size_kb'],  df_test['size_mb'], df_test['size_gb'] = zip(*df_test['size'].apply(sizes))

পরীক্ষার ফলাফল:

Separate df.apply(): 

    100 loops, best of 3: 1.43 ms per loop

Return Series: 

    100 loops, best of 3: 2.61 ms per loop

Return tuple:

    1000 loops, best of 3: 819 µs per loop

আমি আশ্চর্য হয়েছি যে এটি আরও বেশি অগ্রগতি পায় নি। অতিরিক্ত বৈকল্পিক এবং সময়কালীন ডেটা ভাগ করার জন্য ধন্যবাদ।
গিফট

আপনি দয়া করে ব্যাখ্যা করতে পারেন আপনি কীভাবে ফিরে এসেছিলেন? এটি দ্রুততম বিকল্প বলে মনে হচ্ছে
ক্যামিলো

দয়া করে আমার নমুনা কোডটি উল্লেখ করুন, এটি দ্বিগুণ উপায়।
জেসি

খুব দ্রুত এবং সবচেয়ে সহজ বলে মনে হচ্ছে। আশ্চর্য আমি নিজে এটি খুঁজে পেল না।
শাহির আনসারী

65

বর্তমানের কয়েকটি উত্তর ভাল কাজ করে তবে আমি আরও একটি "প্যান্ডিফাইড" বিকল্প দিতে চাই। এটি আমার জন্য বর্তমান পান্ডাস 0.23 নিয়ে কাজ করে (এটি পূর্ববর্তী সংস্করণে কাজ করবে কিনা তা নিশ্চিত নয়):

import pandas as pd

df_test = pd.DataFrame([
  {'dir': '/Users/uname1', 'size': 994933},
  {'dir': '/Users/uname2', 'size': 109338711},
])

def sizes(s):
  a = locale.format("%.1f", s['size'] / 1024.0, grouping=True) + ' KB'
  b = locale.format("%.1f", s['size'] / 1024.0 ** 2, grouping=True) + ' MB'
  c = locale.format("%.1f", s['size'] / 1024.0 ** 3, grouping=True) + ' GB'
  return a, b, c

df_test[['size_kb', 'size_mb', 'size_gb']] = df_test.apply(sizes, axis=1, result_type="expand")

লক্ষ্য করুন যে কৌশলটি result_typeপ্যারামিটারে রয়েছে apply, তার ফলাফলটি এমনভাবে প্রসারিত হবে DataFrameযা সরাসরি নতুন / পুরানো কলামগুলিতে নির্ধারিত হতে পারে।


4
একেবারে ঠিক ... দুঃখিত ... কিছু পরীক্ষণ পর এটা কাজ কিছু দৃষ্টান্ত 0.22 সঙ্গে dos, কিন্তু আমি একটি ভার্চুয়াল পরিবেশে ছিলেন এবং আসলে 0.23 চলমান যখন আমি যে চেষ্টা ...: /
jaumebonet

7
এটি সর্বাধিক অনুকূল উত্তর। ধন্যবাদ
এডিআর

এটি গ্রহণযোগ্য উত্তর হওয়া উচিত!
বি বোগার্ট

18

আর একটি পঠনযোগ্য উপায়। এই কোডটি তিনটি নতুন কলাম এবং এর মান যুক্ত করবে, প্রয়োগ ফাংশনে প্যারামিটার ছাড়াই সিরিজটি ফিরিয়ে দেবে।

def sizes(s):

    val_kb = locale.format("%.1f", s['size'] / 1024.0, grouping=True) + ' KB'
    val_mb = locale.format("%.1f", s['size'] / 1024.0 ** 2, grouping=True) + ' MB'
    val_gb = locale.format("%.1f", s['size'] / 1024.0 ** 3, grouping=True) + ' GB'
    return pd.Series([val_kb,val_mb,val_gb],index=['size_kb','size_mb','size_gb'])

df[['size_kb','size_mb','size_gb']] = df.apply(lambda x: sizes(x) , axis=1)

এর থেকে একটি সাধারণ উদাহরণ: https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.apply.html

df.apply(lambda x: pd.Series([1, 2], index=['foo', 'bar']), axis=1)

#foo  bar
#0    1    2
#1    1    2
#2    1    2

10

সত্যিই দুর্দান্ত উত্তর! ধন্যবাদ জেসি এবং জ্যাম্মোনেট! সম্পর্কিত কিছু পর্যবেক্ষণ:

  • zip(* ...
  • ... result_type="expand")

যদিও প্রসারিত আরো মার্জিত (এর ধরনের pandifyed ), জিপ অন্তত ** 2x দ্রুততর । এই সহজ উদাহরণে নমুনা, আমি 4x দ্রুত পেয়েছি ।

import pandas as pd

dat = [ [i, 10*i] for i in range(1000)]

df = pd.DataFrame(dat, columns = ["a","b"])

def add_and_sub(row):
    add = row["a"] + row["b"]
    sub = row["a"] - row["b"]
    return add, sub

df[["add", "sub"]] = df.apply(add_and_sub, axis=1, result_type="expand")
# versus
df["add"], df["sub"] = zip(*df.apply(add_and_sub, axis=1))

9

শীর্ষ উত্তরের মধ্যে পারফরম্যান্স উল্লেখযোগ্যভাবে বৈচিত্রময়, এবং জেসি এবং ফামারাল 42 ইতিমধ্যে এটি নিয়ে আলোচনা করেছে, তবে এটি শীর্ষ উত্তরের মধ্যে একটি সুষ্ঠু তুলনা ভাগ করে নেওয়া এবং জেসির উত্তরের একটি সূক্ষ্ম তবে গুরুত্বপূর্ণ বিশদটি বিশদভাবে বর্ণনা করা উচিত: যুক্তিটি এতে প্রবেশ করেছে ফাংশন, কর্মক্ষমতা প্রভাবিত করে

(পাইথন 3.7.4, পান্ডাস 1.0.3)

import pandas as pd
import locale
import timeit


def create_new_df_test():
    df_test = pd.DataFrame([
      {'dir': '/Users/uname1', 'size': 994933},
      {'dir': '/Users/uname2', 'size': 109338711},
    ])
    return df_test


def sizes_pass_series_return_series(series):
    series['size_kb'] = locale.format_string("%.1f", series['size'] / 1024.0, grouping=True) + ' KB'
    series['size_mb'] = locale.format_string("%.1f", series['size'] / 1024.0 ** 2, grouping=True) + ' MB'
    series['size_gb'] = locale.format_string("%.1f", series['size'] / 1024.0 ** 3, grouping=True) + ' GB'
    return series


def sizes_pass_series_return_tuple(series):
    a = locale.format_string("%.1f", series['size'] / 1024.0, grouping=True) + ' KB'
    b = locale.format_string("%.1f", series['size'] / 1024.0 ** 2, grouping=True) + ' MB'
    c = locale.format_string("%.1f", series['size'] / 1024.0 ** 3, grouping=True) + ' GB'
    return a, b, c


def sizes_pass_value_return_tuple(value):
    a = locale.format_string("%.1f", value / 1024.0, grouping=True) + ' KB'
    b = locale.format_string("%.1f", value / 1024.0 ** 2, grouping=True) + ' MB'
    c = locale.format_string("%.1f", value / 1024.0 ** 3, grouping=True) + ' GB'
    return a, b, c

ফলাফল এখানে:

# 1 - Accepted (Nels11 Answer) - (pass series, return series):
9.82 ms ± 377 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

# 2 - Pandafied (jaumebonet Answer) - (pass series, return tuple):
2.34 ms ± 48.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

# 3 - Tuples (pass series, return tuple then zip):
1.36 ms ± 62.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

# 4 - Tuples (Jesse Answer) - (pass value, return tuple then zip):
752 µs ± 18.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

লক্ষ্য করুন কীভাবে টিপলগুলি ফেরানো দ্রুততম পদ্ধতি, তবে যা আর্গুমেন্ট হিসাবে পাস হয় তা কার্য সম্পাদনকেও প্রভাবিত করে। কোডের পার্থক্যটি সূক্ষ্ম তবে কার্যকারিতা উন্নতি তাৎপর্যপূর্ণ।

টেস্ট # 4 (একক মান দিয়ে পাস করা) টেস্ট # 3 (একটি সিরিজে পাস করা) এর দ্বিগুণ দ্রুত, যদিও সঞ্চালিত অপারেশনটি অবশ্যই অভিন্ন।

তবে আরও আছে ...

# 1a - Accepted (Nels11 Answer) - (pass series, return series, new columns exist):
3.23 ms ± 141 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

# 2a - Pandafied (jaumebonet Answer) - (pass series, return tuple, new columns exist):
2.31 ms ± 39.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

# 3a - Tuples (pass series, return tuple then zip, new columns exist):
1.36 ms ± 58.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

# 4a - Tuples (Jesse Answer) - (pass value, return tuple then zip, new columns exist):
694 µs ± 3.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

কিছু ক্ষেত্রে (# 1a এবং # 4a), ডেটাফ্রেমে ফাংশন প্রয়োগ করা যেখানে আউটপুট কলামগুলি ইতিমধ্যে বিদ্যমান রয়েছে তা ফাংশন থেকে তৈরি করার চেয়ে দ্রুত।

পরীক্ষা চালানোর কোড এখানে:

# Paste and run the following in ipython console. It will not work if you run it from a .py file.
print('\nAccepted Answer (pass series, return series, new columns dont exist):')
df_test = create_new_df_test()
%timeit result = df_test.apply(sizes_pass_series_return_series, axis=1)
print('Accepted Answer (pass series, return series, new columns exist):')
df_test = create_new_df_test()
df_test = pd.concat([df_test, pd.DataFrame(columns=['size_kb', 'size_mb', 'size_gb'])])
%timeit result = df_test.apply(sizes_pass_series_return_series, axis=1)

print('\nPandafied (pass series, return tuple, new columns dont exist):')
df_test = create_new_df_test()
%timeit df_test[['size_kb', 'size_mb', 'size_gb']] = df_test.apply(sizes_pass_series_return_tuple, axis=1, result_type="expand")
print('Pandafied (pass series, return tuple, new columns exist):')
df_test = create_new_df_test()
df_test = pd.concat([df_test, pd.DataFrame(columns=['size_kb', 'size_mb', 'size_gb'])])
%timeit df_test[['size_kb', 'size_mb', 'size_gb']] = df_test.apply(sizes_pass_series_return_tuple, axis=1, result_type="expand")

print('\nTuples (pass series, return tuple then zip, new columns dont exist):')
df_test = create_new_df_test()
%timeit df_test['size_kb'],  df_test['size_mb'], df_test['size_gb'] = zip(*df_test.apply(sizes_pass_series_return_tuple, axis=1))
print('Tuples (pass series, return tuple then zip, new columns exist):')
df_test = create_new_df_test()
df_test = pd.concat([df_test, pd.DataFrame(columns=['size_kb', 'size_mb', 'size_gb'])])
%timeit df_test['size_kb'],  df_test['size_mb'], df_test['size_gb'] = zip(*df_test.apply(sizes_pass_series_return_tuple, axis=1))

print('\nTuples (pass value, return tuple then zip, new columns dont exist):')
df_test = create_new_df_test()
%timeit df_test['size_kb'],  df_test['size_mb'], df_test['size_gb'] = zip(*df_test['size'].apply(sizes_pass_value_return_tuple))
print('Tuples (pass value, return tuple then zip, new columns exist):')
df_test = create_new_df_test()
df_test = pd.concat([df_test, pd.DataFrame(columns=['size_kb', 'size_mb', 'size_gb'])])
%timeit df_test['size_kb'],  df_test['size_mb'], df_test['size_gb'] = zip(*df_test['size'].apply(sizes_pass_value_return_tuple))

পারফরম্যান্সের বৈশিষ্ট্যগুলিও ভেঙে দেওয়ার জন্য আপনাকে ধন্যবাদ!
পলমেষ্ট

3

আমি বিশ্বাস করি যে ১.১ সংস্করণটি এখানে শীর্ষ উত্তরের প্রস্তাবিত আচরণটি ভঙ্গ করে।

import pandas as pd
def test_func(row):
    row['c'] = str(row['a']) + str(row['b'])
    row['d'] = row['a'] + 1
    return row

df = pd.DataFrame({'a': [1, 2, 3], 'b': ['i', 'j', 'k']})
df.apply(test_func, axis=1)

উপরের কোডটি পান্ডাসের ১.১.০ রিটার্নে চলেছিল:

   a  b   c  d
0  1  i  1i  2
1  1  i  1i  2
2  1  i  1i  2

পান্ডাসে থাকাকালীন ১.০.৫ এটি ফিরে এসেছে:

   a   b    c  d
0  1   i   1i  2
1  2   j   2j  3
2  3   k   3k  4

যা আমি মনে করি আপনি যা প্রত্যাশা করতেন।

রিলিজ নোটগুলি কীভাবে এই আচরণটি ব্যাখ্যা করে তা নিশ্চিত নয় , তবে এখানে যেমন ব্যাখ্যা করা হয়েছে মূল সারিগুলিকে অনুলিপি করে এড়িয়ে যাওয়া পুরানো আচরণকে পুনরুত্থিত করে। অর্থাত:

def test_func(row):
    row = row.copy()   #  <---- Avoid mutating the original reference
    row['c'] = str(row['a']) + str(row['b'])
    row['d'] = row['a'] + 1
    return row

আমি মনে করি আপনার কোড নমুনায় একটি অনুলিপি / পেস্ট ত্রুটি থাকতে পারে। আপনি এটি পরীক্ষা করে দেখতে পারেন যে আপনি যা জমা দেওয়ার ইচ্ছা করেছিলেন তা যদি হয়?
পলমেষ্ট

4
ধন্যবাদ @ পলমেষ্ট আপনি ঠিক বলেছেন। আমি দুটি টাইপগুলি স্থির করেছি এবং একটি নতুন লিঙ্ক / রেফারেন্স যুক্ত করেছি যেখানে প্রশ্নের উত্তর দেওয়া আছে।
moo

4
স্ট্যাক ওভারফ্লোতে স্বাগতম! @ মূ
পলমেষ্ট

1

সাধারণত, একাধিক মান ফিরিয়ে দিতে, আমি এটিই করি

def gimmeMultiple(group):
    x1 = 1
    x2 = 2
    return array([[1, 2]])
def gimmeMultipleDf(group):
    x1 = 1
    x2 = 2
    return pd.DataFrame(array([[1,2]]), columns=['x1', 'x2'])
df['size'].astype(int).apply(gimmeMultiple)
df['size'].astype(int).apply(gimmeMultipleDf)

ডেটাফ্রেমটি ফিরিয়ে দেওয়ার বিষয়ে নিশ্চিতভাবেই এর সুবিধা রয়েছে তবে কখনও কখনও এটির প্রয়োজন হয় না। আপনি কী apply()ফিরে পান তা দেখতে এবং ফাংশনগুলির সাথে কিছুটা খেলতে পারেন;)


এই নমুনার জন্য ধন্যবাদ। যাইহোক, এটি ফলাফলের জন্য একক ডেটাফ্রেম আউটপুট দেয় না। আমি যখন এটি আবার মূল ডেটাফ্রেমে যুক্ত করার চেষ্টা করি তখন আমি "ভ্যালুয়েরর: অ্যারেটি সঠিক আকারের জন্য সম্প্রচারযোগ্য নয়" পাই।
পলমেস্ট

আপনি কিছু ছোট ডেটা নমুনা উত্পাদন কোড সরবরাহ করতে পারেন?
ফুবার

ঠিক. নমুনা ডেটা এবং আউটপুট অন্তর্ভুক্ত করার জন্য আমি আমার মূল পোস্টে কোডটি আপডেট করেছি।
পলমেষ্ট

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.