पांडा के कई कॉलम लागू करें ()


100

मेरे पास एक पांडा डेटाफ़्रेम है df_test,। इसमें एक कॉलम 'आकार' होता है जो बाइट्स में आकार का प्रतिनिधित्व करता है। मैंने निम्नलिखित कोड का उपयोग करके KB, MB और GB की गणना की है:

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]

मैंने इसे १२०,००० से अधिक पंक्तियों में चलाया है और यह समय प्रति स्तंभ २. per ९ सेकंड प्रति कॉलम * ३ = ~ ९ सेकंड लेता है।

वैसे भी क्या मैं इसे और तेज़ बना सकता हूँ? उदाहरण के लिए, क्या मैं एक कॉलम को लागू करने से एक बार में वापस करने और इसे 3 बार चलाने के बजाय, क्या मैं मूल डेटाफ़्रेम में वापस सम्मिलित करने के लिए एक पास के सभी तीन कॉलम वापस कर सकता हूँ?

अन्य प्रश्न जो मैंने पाए हैं, वे कई मान लेना चाहते हैं और एक एकल मान लौटाते हैं । मैं एक एकल मान लेना चाहता हूं और कई कॉलम वापस करना चाहता हूं ।

जवाबों:


114

यह एक पुराना सवाल है, लेकिन पूर्णता के लिए, आप लागू फ़ंक्शन से एक श्रृंखला वापस कर सकते हैं जिसमें नया डेटा शामिल है, तीन बार पुनरावृति करने की आवश्यकता को रोकता है। पासिंग axis=1लागू समारोह के लिए समारोह लागू होता है sizesdataframe की प्रत्येक पंक्ति के लिए, एक नया dataframe में जोड़ने के लिए एक श्रृंखला लौटने। इस श्रृंखला में नए मान, साथ ही मूल डेटा शामिल हैं।

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)

11
मुझे आश्चर्य है कि यह सही जवाब के बिना लगभग 2 साल बिताए। मैं कुछ और ढूंढ रहा था और इस पर ठोकर खाई। आशा है कि उपयोगी होने में देर नहीं हुई!
Nelz11

10
rows_listइस जवाब में क्या है ?
डेविड स्टैंस्बी

यह डेटाफ़्रेम के निर्माण के लिए सिरीज़ की एक सूची है।
नेलज़ 11

1
यदि pd.Series को एक इंडेक्स की आवश्यकता है, तो आपको इसके साथ आपूर्ति करने की आवश्यकता है pd.Series(data, index=...)। अन्यथा जब आप मूल डेटाफ़्रेम में परिणाम वापस असाइन करने का प्रयास करते हैं, तो आपको गुप्त त्रुटियाँ मिलती हैं।
11

94

सीरीज़ के रास्ते की तुलना में 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

मुझे आश्चर्य है कि इसने अधिक उभार नहीं लिए हैं। अतिरिक्त संस्करण और समय डेटा साझा करने के लिए धन्यवाद।
गनमेंट

क्या आप बता सकते हैं कि आपने किस तरह से वापसी की? यह सबसे तेज़ विकल्प लगता है
कैमिलो

कृपया मेरे सैंपल कोड का उल्लेख करें, यह एक अच्छा तरीका है।
जेसी

सबसे तेज और आसान भी लगता है। आश्चर्य है कि मैं इसे खुद नहीं पा सका।
शाहीर अंसारी

53

वर्तमान उत्तरों में से कुछ ठीक काम करते हैं, लेकिन मैं एक और पेशकश करना चाहता हूं, शायद अधिक "पैंडिफ़ाइड" विकल्प। यह मेरे लिए मौजूदा पांडा 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जिसमें सीधे नए / पुराने कॉलम को असाइन किया जा सकता है।


1
यह सही है ... क्षमा करें ... कुछ जाँच के बाद, यह कुछ उदाहरणों में 0.22 के साथ काम करता है, लेकिन मैं एक आभासी वातावरण में था और वास्तव में 0.23 चल रहा था जब मैंने कोशिश की कि ...:
jaumebonet

4
यह सबसे इष्टतम उत्तर है। धन्यवाद
एडीआर

16

बस एक और पठनीय तरीका। यह कोड तीन नए कॉलम और इसके मानों को जोड़ेगा, जो कि लागू फंक्शन में उपयोग मापदंडों के बिना श्रृंखला लौटाएगा।

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

9

वास्तव में अच्छा जवाब! धन्यवाद जेसी और jaumebonet! बस कुछ के संबंध में अवलोकन:

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

हालांकि विस्तार अधिक सुरुचिपूर्ण ( पैंडिफ़ाइड ) की तरह है, ज़िप कम से कम ** 2x तेज है । इस सरल उदाहरण bellow पर, मैं 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))

8

शीर्ष उत्तरों के बीच प्रदर्शन में काफी अंतर है, और जेसी और famaral42 ने पहले ही इस पर चर्चा की है, लेकिन यह शीर्ष उत्तरों के बीच एक निष्पक्ष तुलना साझा करने के लायक है, और जेसी के जवाब के एक सूक्ष्म लेकिन महत्वपूर्ण विवरण पर विस्तार से बताया गया है: यह तर्क पास हो गया फ़ंक्शन, प्रदर्शन को भी प्रभावित करता है

(पायथन 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)

कुछ मामलों में (# 1 ए और # 4 ए), फ़ंक्शन को एक डेटाफ़्रेम में लागू करना जिसमें आउटपुट कॉलम पहले से मौजूद हैं, उन्हें फ़ंक्शन से बनाने की तुलना में तेज़ है।

यहाँ परीक्षण चलाने के लिए कोड है:

# 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))

प्रदर्शन विशेषताओं को तोड़ने के लिए धन्यवाद!
पॉलमेस्ट

2

मेरा मानना ​​है कि 1.1 संस्करण यहां शीर्ष उत्तर में सुझाए गए व्यवहार को तोड़ता है।

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)

उपरोक्त कोड पंडों 1.1.0 रिटर्न पर चला गया:

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

जबकि पांडा 1.0.5 में यह वापस आ गया:

   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

मुझे लगता है कि आपके कोड नमूने में कॉपी / पेस्ट त्रुटि हो सकती है। क्या आप इसे देख सकते हैं और देख सकते हैं कि क्या आपने इसे प्रस्तुत करने का इरादा किया है?
पॉलिमेस्ट

1
धन्यवाद @PaMMest आप सही थे। मैंने दो टाइपो को तय किया और एक नया लिंक / संदर्भ जोड़ा जहां प्रश्न का उत्तर दिया गया है।
मू

1
ढेर अतिप्रवाह में आपका स्वागत है! @ मू
पॉलिमेस्ट

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()रिटर्न क्या है और फ़ंक्शन के साथ थोड़ा सा खेलते हैं;)


इस नमूने के लिए धन्यवाद। हालाँकि, यह सभी परिणामों के लिए एक एकल डेटाफ्रेम का उत्पादन नहीं करता है। जब मैं इसे मूल डेटाफ़्रेम में वापस जोड़ने की कोशिश करता हूं, तो मुझे "ValueError: सरणी सही आकार में प्रसारित नहीं होती है"।
पॉलमेस्ट

क्या आप कुछ छोटे डेटा नमूने का उत्पादन करने के लिए कोड प्रदान कर सकते हैं?
फुआबर

ज़रूर। मैंने अभी नमूना डेटा और आउटपुट को शामिल करने के लिए अपने मूल पोस्ट में कोड अपडेट किया है।
पॉलमेस्ट

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.